코딩하는 해맑은 거북이

[PyTorch] PyTorch 구조1 본문

Python/Tensorflow | PyTorch

[PyTorch] PyTorch 구조1

#CJE 2023. 3. 13.
본 게시물의 내용은 '부스트캠프 AI Tech - PyTorch(최성철)' 강의를 듣고 작성하였다.
해당 글은 아래의 2가지를 다룬다.
1. AutoGrad & Optimizer
  ▶ Layer = Block
  ▶ torch.nn.Module
  ▶ nn.Parameter
  ▶ Backward
  ▶ Backward from the scratch
2. PyTorch datasets & dataloaders
  ▶ Dataset 클래스
  ▶ DataLoader 클래스

 

1. AutoGrad & Optimizer

▶ Layer = Block

- 레고의 Block 같이 하나하나 쌓아서 다음으로 넘긴다. 

- layer들을 합쳐서 하나의 큰 block을 만드는 layer도 존재한다.

  즉, 블록 반복의 연속이다.

ex1) Transformer 모델

(왼: Encoder Layer, 오:Decoder Layer)

 

ex2) ResNet

 

▶ torch.nn.Module

- 딥러닝을 구성하는 Layer의 base class
- Input, Output, Forward, Backward 정의
- 학습의 대상이 되는 parameter(tensor) 정의

 

▶ nn.Parameter

- Tensor 객체의 상속 객체
- nn.Module 내에 attribute가 될 때는 required_grad=True 로 지정되어 학습 대상이 되는 Tensor
- 우리가 직접 지정할 일 은 잘 없음 (대부분의 layer에는 weights 값들이 지정되어 있음)

import torch
from torch import nn
from torch import Tensor
class MyLiner(nn.Module):
    def __init__(self, in_features, out_features, bias=True):
        super().__init__()
        self.in_features = in_features
        self.out_features = out_features
        
        self.weights = nn.Parameter(
                torch.randn(in_features, out_features))
        
        self.bias = nn.Parameter(torch.randn(out_features))

    def forward(self, x : Tensor):
        return x @ self.weights + self.bias
x = torch.randn(5, 7) # data : 5개, feature : 7개
x

layer = MyLiner(7, 12)  # 7개 feature -> 12개 feature
layer(x).shape

for value in layer.parameters():  # 랜덤하게 설정된 Parameter들 = weights
    print(value)

 

* nn.Parameter -> Tensor로 변경하면?

class MyLiner(nn.Module):
    def __init__(self, in_features, out_features, bias=True):
        super().__init__()
        self.in_features = in_features
        self.out_features = out_features
        
        self.weights = Tensor(      # Tensor로 설정하면?
                torch.randn(in_features, out_features))
        
        self.bias = Tensor(torch.randn(out_features))

    def forward(self, x : Tensor):
        return x @ self.weights + self.bias


layer = MyLiner(7, 12)
layer(x).shape

for value in layer.parameters():  # Parameter가 출력되지 않는다! 
    print(value)
# 이유:Parameter는 미분의 대상이 되는 것만 보여줄 수 있기 때문에
# backward propagation의 대상이 되는 것들을 제외한 값들은 출현하지 않는다.

(출력값 없음!)

 

 

▶ Backward

- Layer에 있는 Parameter들의 미분을 수행
- Forward의 결과값 (model의 output=예측치)과 실제값간의 차이(loss) 에 대해 미분을 수행
- 해당 값으로 Parameter 업데이트

for epoch in range(epochs):
    # Converting inputs and labels to Variable
    if torch.cuda.is_available():
        inputs = Variable(torch.from_numpy(x_train).cuda())
        labels = Variable(torch.from_numpy(y_train).cuda())
    else:
        inputs = Variable(torch.from_numpy(x_train))
        labels = Variable(torch.from_numpy(y_train))

    # Clear gradient buffers because we don't want any gradient ~
    # from previous epoch to carry forward, dont want to cummulate gradients
    # 이전의 gradient 값이 지금의 학습에서 영향을 주지 않도록 하기 위함
    optimizer.zero_grad()               # 1단계

    # get output from the model, given the inputs
    # hat(y)
    outputs = model(inputs)

    # get loss for the predicted output
    # hat(y)와 y의 loss 값
    loss = criterion(outputs, labels)   # 2단계
    print(loss)
    # get gradients w.r.t to parameters
    loss.backward()                     # 3단계

    # update parameters
    optimizer.step()                    # 4단계

    # 학습을 위해선 위와 같은 4단계 과정을 꼭 거쳐야한다.
    print('epoch {}, loss {}'.format(epoch, loss.item()))

 

 

▶ Backward from the scratch

- 실제 backward는 Module단계에서 직접 지정가능 (그러나, AutoGrad가 알아서 하기때문에 직접 지정할필요가 없음)
- Module에서 backward와 optimizer 오버라이딩
- 사용자가 직접 미분 수식을 써야하는 부담

  → 쓸 일은 없으나 순서를 이해할 필요는 있음

class LR(nn.Module):
    def __init__(self, dim, lr=torch.scalar_tensor(0.01)):
        super(LR, self).__init__()
        # intialize parameters
        self.w = torch.zeros(dim, 1, dtype=torch.float).to(device)
        self.b = torch.scalar_tensor(0).to(device)
        self.grads = {"dw": torch.zeros(dim, 1, dtype=torch.float).to(device),
                      "db": torch.scalar_tensor(0).to(device)}
        self.lr = lr.to(device)

    # hat(y)
    def forward(self, x):
        ## compute forward
        z = torch.mm(self.w.T, x) + self.b
        a = self.sigmoid(z)
        return a

    def sigmoid(self, z):
        return 1/(1 + torch.exp(-z))

    # 미분
    def backward(self, x, yhat, y):
        ## compute backward
        self.grads["dw"] = (1/x.shape[1]) * torch.mm(x, (yhat - y).T)
        self.grads["db"] = (1/x.shape[1]) * torch.sum(yhat - y)
    
    # 미분값 만큼 업데이트해주는 함수
    def optimize(self):
        ## optimization step
        self.w = self.w - self.lr * self.grads["dw"]
        self.b = self.b - self.lr * self.grads["db"]

## utility functions
def loss(yhat, y):
    m = y.size()[1]
    return -(1/m)* torch.sum(y*torch.log(yhat) + (1 - y)* torch.log(1-yhat))

forward 함수
backward 함수
optimize 함수

 

 

2. PyTorch datasets & dataloaders

▶ Dataset 클래스

- 데이터 입력 형태를 정의하는 클래스
- 데이터를 입력하는 방식의 표준화
- Image, Text, Audio 등에 따른 다른 입력정의

 

* Dataset 클래스 생성시 유의점

- 데이터 형태에 따라 각 함수를 다르게 정의함
- 모든 것을 데이터 생성 시점에 처리할 필요는 없음

  → image의 Tensor 변화는 학습에 필요한 시점에 변환
- 데이터 셋에 대한 표준화된 처리방법 제공 필요
  → 후속 연구자 또는 동료에게는 빛과 같은 존재
- 최근에는 HuggingFace 등 표준화된 라이브러리 사용

 

 

▶ DataLoader 클래스

- Data의 Batch를 생성해주는 클래스
- 학습직전 (GPU feed전) 데이터의 변환을 책임
- Tensor로 변환 + Batch 처리가 메인 업무
- 병렬적인 데이터 전처리 코드의 고민 필요

 

* DataLoader 파라미터별 설명

DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, collate_fn=None, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None, * , prefetch_factor=2, persistent_workers=False)

https://subinium.github.io/pytorch-dataloader/

 

[Pytorch] DataLoader parameter별 용도

pytorch reference 문서를 다 외우면 얼마나 편할까!!

subinium.github.io

'Python > Tensorflow | PyTorch' 카테고리의 다른 글

[PyTorch] 사칙연산  (0) 2023.03.17
[PyTorch] PyTorch 활용하기  (0) 2023.03.16
[PyTorch] PyTorch 구조2  (0) 2023.03.15
[PyTorch] PyTorch 기본  (0) 2023.03.13
[Tensorflow] Tensorflow 정의 및 동작 방식  (0) 2021.03.19
Comments