본문 바로가기
AI 컴퓨터 비전프로젝트/[ML,DL]머신러닝,딥러닝

[ML/DL] 파이토치로 구현한 선형회귀_단항,최적화, 다중 선형회귀

by 바다의 공간 2024. 7. 18.

1.단항 선형 회귀

  • 한 개의 입력이 들어가서 한 개의 출력이 나오는 구조
import torch
import torch.nn as nn 
import torch.optim as optim
import matplotlib.pyplot as plt
torch.manual_seed(2024)

파이토치에서 토치 변수가 랜덤한값을 2024로 고정하는 것입니다

 

.데이터를 3개를 만들었습니다. 3번들어가는겁니다. 

레이블이 3개! 

1->2 

2->4

3->6 이렇게 되는거죠

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)

시각화하기

plt.figure(figsize=(6,4))
plt.scatter(x_train, y_train)

모델만들기 Linear(입력받는 개수, 출력하는 개수) 를 이용해서 

model = nn.Linear(1,1)을 입력했습니다.

# y = Wx + b
model = nn.Linear(1, 1)#입력을 1개를 받아서 출력을 1개 해주겠다는 의미
model

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

bias=False를 사용하면 절편을 안쓰게됨.

y_pred = model(x_train)
y_pred

값은 랜덤으로 나오게됩니다. 이유는 아직 x_train학습이 전혀 되지않았기에
랜덤값이 나옵니다.
그래서 잘 맞추고 있다고 할 수 없겠죠.

 

일단, 위에 y_pred 결과가 어떻게 나왔는지 확인하기 위해서 직접 계산도 해보았습니다.

 

파라미터를 찍어보고 W과 b의 값을 넣어서 계산을 해보았고 그 값이 y_pred값과 맞는지도 확인했습니다.

list(model.parameters()) # W: 0.0634, b: 0.6625

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

# y = Wx+b
# x=1, 0.0634 * 1 + 0.6625 = 0.7259

#x=2일때는 0.0634*2 + 0.0625 = 0.7893

그렇지만 아직 x_train 학습이 되지않았기때문에 잘 맞추고 있다고 확신할 수 없습니다. 

그래서 학습을 시켜주려고 합니다.

 

먼저 실제값과 예측값을 비교를 해봐야하는데 오차를 계산해주는 방법인 MSE를 활용하게 됩니다.

#MSE 공식
((y_pred - y_train)**2).mean()
#tensor(12.8082, grad_fn=<MeanBackward0>)

예측값- 실제값을 뺀 후 제곱을 해주고 평균을 내줍니다.

#mse함수를 구해주는 함수
loss = nn.MSELoss()(y_pred, y_train)
loss
#1번 괄호의미 == nn.MSELoss()
#2번 괄호의미 == (예측값, 학습값)

#tensor(12.8082, grad_fn=<MseLossBackward0>)

*헷갈려서 다시 메모

즉 볼때는 nn.MSELoss() 객체생성 후 (y_pred, y_train)를 실행한다고 보면됩니다.

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

 

의 의미와 같습니다. 만들어진 클래스가 실제 내부적으로 __call이라는 매소드를 가지고있기때문에 실행됩니다.


즉 손으로 계산한거랑 함수를 사용해도 값이 같음을 확인할 수 있습니다.

이제 오차값을 최소로 하는 값을 찾아내야하고 그것이 중요합니다.!!!!


2.최적화(Optimization)

  • 학습 모델의 손실함수(loss function)의 최소값을 찾아가는 과정
  • 학습 데이터를 입력하여 파라미터를 걸쳐 예측 값을 받음 -> 예측값과 실제 정답과 차이를 비교하는 것이 손실함수이고, 예측값과 실젯값 차이를 최소화 하는 파라미터를 찾는 과정이 <최적화>라고 합니다.

= loss를 최소로 만드는 과정을 최적화라고 합니다.


2-1. 경사하강법(Gradient Descent)

  • 딥러닝 알고리즘 학습 시 사용되는 최적화 방법 중 하나
  • 최적화 옵티마이저 알고리즘을 통해서 최적의 W와 b를 찾아내는 과정을 "학습"이라고 부릅니다.

어느쪽으로 가면 cost값이 낮아질지 방향을 찾게 됩니다. 방향을 찾게 되지만 어떤 값을 넣었을때 가장최적의 값을 찾을 수 있는지는 아무도 모르기때문에 Increment Step(학습률)을 이용해서 조금씩 w을 수정하게 됩니다.

 


2-2. 학습률(Learning rate) = lr

*학습률은 값을 내가 직접 주는것입니다.그래서 엉뚱하게 넣으면 제대로 값이 안나올 수 있습니다.

 

  • 한 번의 W를 움직이는 거리(increment step)
  • 0 ~ 1 사이의 실수값
  • 학습률이 너무 크면 한 지점으로 수렴하는것이 아니라 발산할 가능성이 존재합니다.
  • 그렇다고 너무 작은 학습률을 택할 때에는 수렴이 늦어지고, 시작점을 어디로 잡느냐에 따라 수렴 지점이 달라집니다.

너무 촘촘하게 값을 주다보면 global minimum임에도 불구하고 local minimum이 가장 최적값이라고 생각할 수 있다.

그래서 loss값을찍다가 내려갔다가 올라갔다가 이럴수도있다는것을 확인할 수 있습니다.

아직 경험이 많이 없는 저는 발산된다고 생각할 수 있지만 경험이 쌓이다보면 

저런 곡선을 넘는것이라는것을 알 수 있습니다.

 

 


2-3. 경사 하강법의 한계

  • 많은 연산량과 컴퓨터 자원을 소모함
  • 데이터(입력값) 하나가 모델을 지날 때마다 모든 가중치를 한 번씩 업데이트 함
  • 이때 가중치가 적은 모델의 경우 문제가 없으나, 모델의 가중치가 매우 많다면 모든 가중치에 대해 연산을 적용하기 때문에 많은 연산량을 요구할 수 있습니다.
  • Global Minimum은 목표 함수 그래프 전체를 고려했을 때 최솟값을 의미하고, Local Minimum은 그래프 내 일부만 고려햇을 때 최솟값을 의미 -> 경사 하강법으로 최적의 값인 줄 알았던 값이 Local Minimum으로 결과가 나올 수 있음.

 

경사 하강법의 종류중 하나인 

종류1. SGD(Stochastic Grdient Descemt) :

1.랜덤하게 데이터를 하나씩 뽑아서 loss를 만듦.

2.그 다음 데이터를 넣고 다시 데이터를 뽑고를 반복합니다 (랜덤포레스트 데이터 뽑는것과 비슷)

장점은 빠르게 방향을 결정할 수 있습니다.

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

optimizer객체를 생성했습니다.

아까 위에서 수기로loss를 만들었지만 이것을 객체로 만들 수 있죠.

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

예측값과 실제값을 연산할 수 있게 loss객체를 만들어놓았습니다.

여기까지 준비는 완료!되었습니다. 

1.옵티마이저.

2.오차함수 준비완료  

 

학습을 해야하고 처음으로 초기화를 합니다.

optimizer 초기화
optimizer.zero_grad()

optimizer을 초기화 해주는 이유는 loss값이 누적되어서 보이기에 학습이 제대로 되지않기때문입니다.

 

**초기화 할 경우에 아래와같이 됩니다.

학습1) model training loss : 1.5
학습2) model training loss : 1.4
학습3) model training loss : 1.3
....

 

**초기화 하지 않을 경우에 아래와같이 됩니다.

학습1) model training loss : 1.5
학습2) model training loss : 2.9
학습3) model training loss : 4.2
....

 

이렇게 되면 안되기에 loss.backward()를 사용하고 

loss.backward() 호출될 때 초기설정은 gradient를 더해주는 것으로 되어있습니다.
학습 loop를 돌 때 이상적으로 학습이 이루어지기 위해서 한 번의 학습이 완료되어지면 LOSS값이 누적되지 않도록
gradient를 항상 0으로 만들어주어야 합니다.

optimizer.zero_grad()

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

#가중치 업데이트 함수 : 계산된 gradient를 사용하여 파라미터를 업데이트합니다.
optimizer.step()

 

위 3개의 코드는 항상 붙어다닌다는것을 확인하기!!

# list(model.parameters()) # W:0.0634 b: 0.6625
list(model.parameters()) # W:0.2177 b: 0.7267

웨이트와 바이어스가 변경된것을 확인할 수 있습니다.

 

반복학습을 통해 오차가 있는 W,b 를 수정하면서 오차를 계속 줄여나감

# epochs: 반복 학습 횟수(에포크)
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}')

 

100바퀴마다 한번씩 loss값을 찍어주는것을 확인하겠습니다.

loss:.6f = 소수 6자리까지라는 의미입니다.

에포크를 돌릴때마다 loss값이 많이 줄어드는것을 확인할 수 있씁니다.

 

아래는 파라미터를 확인하고있습니다.

print(list(model.parameters())) #W:1.9499, b:0.1138

x_test = torch.FloatTensor([[5]])
y_pred = model(x_test)
y_pred

5를 넣고 답을 10을 예측했지만 우리는 9.8이 나왔으니 어느정도 잘 맞추고 있다는것을 확인할 수 있습니다.


 

3. 다중 선형 회귀

  • 여러 개의 입력이 들어가서 한 개의 출력이 나오는 구조
X_train = torch.FloatTensor([[73, 80, 75],
                             [93, 88, 93],
                             [89, 91, 90],
                             [96, 98, 100],
                             [73, 66, 70]])
y_train =torch.FloatTensor([[220], [270], [265], [290], [200]])

이것같은경우는 3개가 들어가서 1개가 나오는 구조입니다.

 

print(X_train, X_train.shape)
print(y_train, y_train.shape)

 

이것들을 모델로 넣어서 학습시키려고 합니다.

입력은 3개 출력은 1개가 되는 모델입니다.

# y= W1x1 + W2x2 + WW3x3 + b
model = nn.Linear(3,1)
model

#Linear(in_features=3, out_features=1, bias=True)
optimizer = optim.SGD(model.parameters(), lr=0.00001)
epochs = 20000

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}')
list(model.parameters())#W: [[0.6814, 0.8616, 1.3889]], b:[-0.2950]
x_test = torch.FloatTensor([[100, 100, 100]])
y_pred = model(x_test)
y_pred

#tensor([[292.8991]], grad_fn=<AddmmBackward0>)

완벽한 답으로는 300이 나와야하지만 292.9정도가 나온것을 보니 학습 및 예측이 잘 되는것으로 확인됩니다.


4. temps.csv 데이터에서 기온에 따른 지면 온도를 예측하기

#강사님 코드

import pandas as pd

df = pd.read_csv('경로)
df.info()

df.isnull().mean()

df=dr.dropna()

df.isnull().mean()

x_data = df[['기온(°C)']]
y_data = df[[지면온도(°C)']]

x_data = torch.FloatTensor(x_data.values)
y_data = torch.FloatTensor(y_daya.values)
print(x_train, x_train.shape)
print(y_train, y_train.shape)

x_data
y_daya

plt.figure(figsize=(8, 6))
plt.scatter(x_data, y_data)

model = nn.Linear(1,1)
optimizer = optim.SGD(model.parameters(), lr=0.001)
list(model.parameters())#W:-0.5700, b:0.2403


epochs= 30000

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

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

y_pred = model(x)Daya)
y_pred

plt.figure(figsize=(8,6))
plt.scatter(x_data, y_data)
plt.scatter(x_data, y_pred.detach().numpy())

#기온 만들기
result = model(torch.FloaatTensor([[40]]))
result

 

#내가 짠 코드

#판다스 임포트
import pandas as pd

#csv파일 가져오기 & 확인
df = pd.read_csv('경로')
df

#필요없는 데이터들 drop
df.drop(columns=['지점', '지점명', '일시'],inplace=True)

#파일 내 가지고있는 데이터로 학습
x_train = torch.FloatTensor([[-8.7], [-7.3], [-6.7],
                            [-6.2], [-5.9]])
y_train = torch.FloatTensor([[-2.9], [-2.4], [-2.2],
                             [-2.0], [-1.9]])

#사이즈확인
print(x_train, x_train.shape)
print(y_train, y_train.shape)

#학습모델 만들기
model = nn.Linear(1,1)
model

#학습
optimizer = optim.SGD(model.parameters(), lr=0.001)

epochs= 10000

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}')

list(model.parameters())


# 새로운데이터넣고 예측
x_test = torch.FloatTensor([-8.8])
y_pred = model(x_test)
y_pred

 

 

torch.nn 모델관련 껍데기 제공
torch.optim 학습하는 모듈(가중치:웨이트값을 예측할 수 있는 것)
torch.manual_seed() 랜덤값을 고정시켜줌
nn.MSELoss()(예측값, 학습값) MSELoss함수
optim.SGD(model.parameters(), lr=0.01) optim안에 SGD라는 함수(model.parameters(), 예측률=0~1사이 값)
optimizer.zero_grad() 옵티마이저 초기화
loss.backward() (역전파) 호출될 때 추기설정은 gradient를 더해주는것으로 되어있음
optimizer.step() 가중치 업데이트 함
torch.FloatTensor([])