Import


import numpy as np

import pandas as pd

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

자주쓰는 기능들

optimizer.zero_grad() : 미분을 통해 얻은기울기 0 으로 초기화

cost.backward() : 비용함수를 미분하여 gradient계산

optimizer.step : 업데이트

단순 y = wx+b


# 데이터
x_train = torch.FloatTensor([[1], [2], [3]])
y_train = torch.FloatTensor([[2], [4], [6]])
# 모델 초기화
W = torch.zeros(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# optimizer 설정
optimizer = optim.SGD([W, b], lr=0.01)

nb_epochs = 1999 # 원하는만큼 경사 하강법을 반복
for epoch in range(nb_epochs + 1):

    # H(x) 계산
    hypothesis = x_train * W + b

    # cost 계산
    cost = torch.mean((hypothesis - y_train) ** 2)

    # cost로 H(x) 개선
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()

    # 100번마다 로그 출력
    if epoch % 100 == 0:
        print('Epoch {:4d}/{} W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
            epoch, nb_epochs, W.item(), b.item(), cost.item()
        ))

  1. optimizer = optim 여기서 이미 얘는 optimizer의 성격을 가지고
  2. zero_grad() 매 epoch마다 값이 변경되므로, 초기화를 시켜준다
  3. cost = torch.mean h-y 의 값을 cost로 가진다.
  4. cost.backward() 계산한 cost값을 기준으로 역전파를 진행한다
  5. optimizer.step() 미리 선언한 SGD를 기준으로 변수 업데이트를 진행한다

역전파 예제


import torch
w = torch.tensor(2.0, requires_grad=True)
y = w**2
z = 2*y + 5
z.backward()
print(z) # 13
print(z.grad) # None
print(w.grad) #8

  1. require_grad=True에서 w를 업데이트시키겠다고 하는게 보임
  2. z = 2 * y + 5 = 2 * (w**2) + 5 라는 수식임
  3. z.backward()를 했는데
  4. z.grad를 찍어보면 오히려 기울기가 안나오고
  5. w 오히려 w.grad로 기울기가 나옴
  6. 이유는 w변수가 requires_grad=True, 미분이 가능한 변수기 때문

loss가 2개??


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]])

# 모델 초기화
W = torch.zeros((3, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# optimizer 설정
optimizer1 = optim.SGD([W, b], lr=1e-5)

nb_epochs = 20
for epoch in range(nb_epochs + 1):

    # H(x) 계산
    # 편향 b는 브로드 캐스팅되어 각 샘플에 더해집니다.
    hypothesis = x_train.matmul(W) + b

    # cost 계산
    cost1 = torch.mean((hypothesis - y_train) ** 2)
    cost2 = torch.mean((hypothesis - y_train) +2)
    # cost로 H(x) 개선
    optimizer1.zero_grad()
    cost1.backward()
    cost2.backward()
    optimizer1.step()

    print('Epoch {:4d}/{} hypothesis: {} Cost: {:.6f}'.format(
        epoch, nb_epochs, hypothesis.squeeze().detach(), cost.item()
    ))
  1. W, b는 변수, requires_grad=True 에 주목
  2. 원래같으면 cost를 계산하는 수식이 1개인데, 여기서는 cost1, cost2를 만들어놨음(???)
  3. optimizer1.zero_grad() 기울기를 초기화 시켜주는 과정
  4. 만들어둔 cost1, cost2를 모두 backward() 시켜줌
  5. optimizer1.step() 으로 학습을 진행시켜줌

결과

RuntimeError: Trying to backward through the graph a second time, but the saved intermediate results have already been freed. Specify retain_graph=True when calling backward the first time

 

왜 안될까?

딥러닝의 한계, backpropagation은 2개이상의 로스에 대해서는 제공하지않음

 

어떻게 고쳤으면 되었을까?

cost3 = (cost1+cost2).backward()

한땀한땀 쌓는 과정 nn.Linear


class LinearRegressionModel(nn.Module): # torch.nn.Module을 상속받는 파이썬 클래스
    def __init__(self): #
        super().__init__()
        self.linear = nn.Linear(3, 2) # 단순 선형 회귀이므로 input_dim=3, output_dim=2

    def forward(self, x):
        return self.linear(x)

해당 소스코드는

model = nn.Linear(in_features=3, out_features=2) 이 소스코드와 동일한 기능을 함

nn.Linear는 레이어 한 줄을 의미함

 

데이터에 대한 DataLoader예제 (모델구성)


from torch.utils.data import TensorDataset # 텐서데이터셋
from torch.utils.data import DataLoader # 데이터로더

dataset= TensorDataset(x_train,y_train)
dataloader = DataLoader(dataset)

class modelex1 (nn.Module) :
    def __init__(self) :
        super().__init__()
        self.layer1 = nn.Linear(in_features=3, out_features=1)

    def forward(self, x):
        return self.layer1(x)

  1. x_train과 y_train을 모두 TensorDataset에 넣고
  2. TensorDataset을 DataLoader에 넣는다
  3. super().__init__() 를 통해 torch.nn.Module를 상속받음
  4. 모델은 단순하게 in(3) → out(1) 의 단순한구조
  5. forward() 에서 모델의 학습을 진행시킴
  • dataloader = DataLoader(x_train,y_train) 해봤는데 에러난다

데이터에 대한 DataLoader예제 (학습진행)


model = modelex1()
#model = nn.Linear(3,1)
optimizer = torch.optim.SGD(model.parameters(), lr=1e-5)
nb_epochs = 20
for epoch in range(nb_epochs + 1):
  for batch_idx, samples in enumerate(dataloader):
    # print(batch_idx)
    # print(samples)
    x_train, y_train = samples
    # H(x) 계산
    prediction = model(x_train)

    # cost 계산
    cost = F.mse_loss(prediction, y_train)

    # cost로 H(x) 계산
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()

    print('Epoch {:4d}/{} Batch {}/{} Cost: {:.6f}'.format(
        epoch, nb_epochs, batch_idx+1, len(dataloader),
        cost.item()
        ))
  • 모델은 아까 짜줬음
  1. Optimizer를 SGD로 선택함
  2. for문을 통해서 학습 진행
  3. for batch_idx, samples in enumerate(dataloader) 로 데이터로더에서 꺼낸다
  4. bacth_idx로 횟수를 확인해내는거고
  5. x_train, y_train = samples 로 데이터를 따로 받아서 설정한다

nn.Sequential의 간편함


model = nn.Sequential(
   nn.Linear(2, 1), # input_dim = 2, output_dim = 1
   nn.Sigmoid() # 출력은 시그모이드 함수를 거친다
)
  • 이전까지는 모델을 짤때 nn.Linear로 한땀한땀 모델을 짜줬었다
  1. nn.Sequential() 을 만들어서. 한번에 여러개의 층을 만든다
  2. 해당 model에는 nn.Linear(2,1) nn.Sigmoid()를 포함한다

nn.Squential이 없다면


class BinaryClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(2, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        return self.sigmoid(self.linear(x))

위에 두개는 동일한 기능을 한다

TRAIN과정


def train(epochs) :
	model.train() #모델 학습모드
	for data, target in dataLoader :
		optimizer.zero_grad
		outputs= model(data)
		loss = loss_fn(outputs, targets)
		loss.backward()
		optimizer.step()
  1. model.train 에서 train을 하겠다는 의지를 보여준다
  2. for data, target in dataLoader 데이터로더에서 데이터를 가져온 이후
  3. optimizer.zero_grad 기울기를 0으로 초기화
  4. outputs 은 data를 model에 거친 결과, 예측값
  5. loss는 예측값, 실제값오차
  6. loss.backward() 오차에 대한 역전파 진행
  7. optimizer.step() 학습을 진행

TEST과정


def test(epochs):
	model.eval() #모델 평가모드
	with torch.no_grad() : # 기울기를 구할필요가없음 # 미분을사용하지않음 #기울기를 0으로 고정
		for data,targets in dataLoader :
			outputs = model(data)
jjongguet