24년 1월 30일에 새로이 update된 Pytorch 2.2는 여러 변경사항이 존재한다.

 

📌 목차

1. TorchRL
2. TorchVision - Transform v2

3. TorchVision - torch.compile()

 

🧐  Summary

Pytorch 2.2는 여러 도메인에서 아래와 같은 Update가 있었다:

 

 

 

 

 

 

 


1. TorchRL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


2. TorchVision - Transform v2

torchvision.transform.v2

transform v2가 드디어 오픈되었다.
간단한 소개?정도와 내용을 조금만 알아보려 한다.



∙ V2  API  Reference



Examples

 Classification

import torch
from torchvision.transforms import v2

H, W = 32, 32
img = torch.randint(0, 256, size=(3, H, W), dtype=torch.uint8)

transforms = v2.Compose([
    v2.RandomResizedCrop(size=(224, 224), antialias=True),
    v2.RandomHorizontalFlip(p=0.5),
    v2.ToDtype(torch.float32, scale=True),
    v2.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
img = transforms(img)


∙ Detection

# Detection (re-using imports and transforms from above)
from torchvision import tv_tensors

img = torch.randint(0, 256, size=(3, H, W), dtype=torch.uint8)
boxes = torch.randint(0, H // 2, size=(3, 4))
boxes[:, 2:] += boxes[:, :2]
boxes = tv_tensors.BoundingBoxes(boxes, format="XYXY", canvas_size=(H, W))

transforms = v2.Compose(
    [
        v2.ToImage(),
        v2.RandomPhotometricDistort(p=1),
        v2.RandomZoomOut(fill={tv_tensors.Image: (123, 117, 104), "others": 0}),
        v2.RandomIoUCrop(),
        v2.RandomHorizontalFlip(p=1),
        v2.SanitizeBoundingBoxes(),
        v2.ToDtype(torch.float32, scale=True),
    ]
)

# The same transforms can be used!
img, boxes = transforms(img, boxes)
# And you can pass arbitrary input structures
output_dict = transforms({"image": img, "boxes": boxes})

아래 Reference Code참고) Pytorch Starting with Transform V2
 

Transforming and augmenting images — Torchvision main documentation

Shortcuts

pytorch.org

 

 

 

 

 

 

 

 

 


3.  TorchVision - torch.compile( )

torch.compile()

torch.compile()의 update는 점점 그래프나누기를 줄이고 동적형태를 허용한다.
transform측면에서 대부분 저수준커널(ex. resize, crop, 등)은
그래프 중단없이 동적으로 compile되어야한다.


사용목적:

∙ Tensorflow의 model.compile:

함수 인자로 Optimizer, Loss, Metric을 사용한다.
model.compile(optimizer="Adam", loss="mse", metrics=["mae"])


∙ Pytorch의 torch.compile:

기존 학습코드자체는 그대로 유지.
Pytorch가 기존에 작동하던 C++베이스→Python상에서 구동되게 함.

[적용되는 기술들]:
  • TorchDynamo : Python frame evaluation hooks를 사용해 pytorch 안정성에 기여.
    정확히는 아직 잘 모르겠지만 역전파 같은 graph capture에서 도움이 된다.

  • AOTAutograd : 기존 pytorch의 Autograd 엔진을 오버로딩한다.
    미리 연산된 역연산 trace를 생성하기 위해 자동미분을 예측한다.

  • PrimTorch : 완벽한 PyTorch 백엔드를 구축하기 위해 개에 달하는
    pytorch 연산들을 개의 기본 연산들로 canonicalize한다.

  • TorchInductor : 여러 액셀러레이터 및 백엔드용 고속 코드를 생성하는 딥러닝 컴파일러.
    NVIDIA GPU의 경우 OpenAI Triton을 주요 구성 요소로 사용함.
위의 그래프는 pytorch개발자들이 진행한 실험의 결과그래프이다.
(구체적으로 46개의 HuggingFace Transformer 모델들, 61개의 TIMM model들 그리고 56개의 TorchBench 모델들)로 실험을 진행.

[결과]:
단순히 torch.compile으로 wrapping해준 것만으로 모델 Training이 43%빠른속도로 동작했다.
(다만, 이는 A100으로 측정된 결과이고, 3090같은 시리즈는 잘 동작하지 않고 심지어 더 느릴 수 있다 언급:
Caveats: On a desktop-class GPU such as a NVIDIA 3090, we’ve measured that speedups are lower than on server-class GPUs such as A100. As of today, our default backend TorchInductor supports CPUs and NVIDIA Volta and Ampere GPUs. It does not (yet) support other GPUs, xPUs or older NVIDIA GPUs.)

이를 2.2버전에서는 좀 더 완성시킨 것이다!

pytorch개발자분들은 버전이 2.x로 넘어가면서 compile함수에 좀 더 집중한다하였다.
아마 점점 학습속도를 빠르게하는 면을 강화하고, 이를 점차 확대할 것 같다.
(이번에 저수준커널에도 적용한 걸 보면 거의 확실시 되는듯하다.)



개발동기:

17년 시작된 이후, Eager Execution성능향상을 위해 코드 대부분을 C++로 옮기게 되었다.
(Pytorch 대부분의 소스코드가 C++기반임을 근거로 알 수 있다.)
(eager execution: 그래프생성없이 연산을 즉시실행하는 환경)

이런 방식을 사용자들의 코드기여도(hackability)를 낮추는 진입장벽이 되어버렸다.
이런 eager execution의 성능향상에 한계가 있다 판단하여 compiler를 만들게 되었다.
목적은 속도는 빠르게하나 pytorch experience를 해치지 않는다는 것이다.

 

🤔 How to use?

torch.compile()은 기존 모델에 한줄만 추가하면 된다.
import torch
import torchvision.models as models

model = models.resnet18().cuda()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
compiled_model = torch.compile(model)

x = torch.randn(16, 3, 224, 224).cuda()
optimizer.zero_grad()
out = compiled_model(x)
out.sum().backward()
optimizer.step()

compiled_model은 신경망의 forward를 좀 더 최적화시켜 속도를 빠르게 한다.

def torch.compile(model: Callable,
  *,
  mode: Optional[str] = "default",
  dynamic: bool = False,
  fullgraph:bool = False,
  backend: Union[str, Callable] = "inductor",
  # advanced backend options go here as kwargs
  **kwargs
) -> torch._dynamo.NNOptimizedModule

∙ mode:

torch.compile(model)

torch.compile(model, mode="reduce-overhaed")

torch.compile(model, mode="max-autotune")
[default]:
너무 오래걸리지 않으면서 메모리를 많이 사용하지 않는 선에서 효율적인 컴파일 진행

[reduce-overhead]:
메모리를 좀 더 사용, overhead를 줄여줌

[max-autotune]:
가장 빠른 모델생성을 위해 최적화되어있다.
다만, 컴파일에 매우 오랜시간이 걸린다.


∙ dynamic:

dynamic shape에 대해 code path를 enabling할 지 결정하는 boolean 변수이다. Compiler 최적화 과정이 프로그램을 dynamic shape 프로그램에 적용될 수 없게 만드는 경우가 있는데, 이를 조절함으로써 본인이 원하는 방향대로 컴파일을 할 수 있게 해준다. 이 부분은 아직 완벽히 이해가 되지는 않지만 데이터 shape가 변하는 상황에서 graph를 유동적으로 컴파일할 수 있게끔 하는 것과 관련이 있을 것 같다.

∙ fullgraph:

Numba의 nopython과 유사하다. 전체 프로그램을 하나의 그래프로 컴파일하고, 만약 실패한다면 왜 불가능한지 설명해주는 error 메세지를 띄운다. 굳이 쓰지 않아도 상관없는 옵션.

∙ backend:

어떤 compiler backend를 적용할 지 결정하게 된다. 디폴트로 정해진 값은 앞서 설명했던 TorchInductor가 사용되지만, 다른 옵션들도 존재한다고 한다(제대로 알아보진 않았다).


❗️ 유의점:

compile된 모델 저장 시, state_dict만 저장가능하다.

∙ 아래는 가능!
torch.save(opt_model.state_dict(), "best.pt")
torch.save(model.state_dict(), "best.pt")
torch.save(model, "best.pt")


∙ 아래는 불가능!
torch.save(opt_model, "best.pt")

 

 

Compile 이후 사용가능한 기능:

 

∙ TorchDynamo

Eager Mode의 가장 용이한 점: 
학습도중 model_weight에 접근하거나 값을 그대로 읽어올 수 있다.
model.conv1.weight

TorchDynamo
는 이를 인지하고 만약 attribute가 변한것을 감지하면
자동으로 해당부분에 대한 변화를 다시 컴파일해준다.



∙ Inference

compile함수로 compiled_model을 생성한 후 warm-up step은 초반 latency를 줄여준다.
다만, 이부분도 차차 개선시킨다 하였다.
exported_model = torch._dynamo.export(model, input)
torch.save(exported_model, "foo.pt")

 

 

 

 

Pytorch Tutorials

https://tutorials.pytorch.kr/

 

파이토치(PyTorch) 한국어 튜토리얼에 오신 것을 환영합니다!

파이토치(PyTorch) 한국어 튜토리얼에 오신 것을 환영합니다. 파이토치 한국 사용자 모임은 한국어를 사용하시는 많은 분들께 PyTorch를 소개하고 함께 배우며 성장하는 것을 목표로 하고 있습니다.

tutorials.pytorch.kr

 

 

 

Pytorch_Brief Tutorials

 

0. Why PyTorch?

🤔 Tensorflow:
∙Google에서 개발
∙딥러닝전용하드웨어인 TPU를 갖고 있어 GPU에서 상대적으로 자유로움

🤔 Pytorch:
∙facebook의 주도하에 개발, 
∙Nvidia의 cuda GPU에 더욱 최적화
∙Python First, 깔끔한 코드, numpy와의 호환성, Autograd, Dynamic Graph 등의 장점

 

 

1. Tensor

pytorch의 tensor는 numpy의 배열인 ndarray와 같은 개념으로
pytorch에서 연산을 수행하기 위한 가장 기본적인 객체이다. (앞으로의 모든 연산은 이 tensor 객체를 통해서 진행)

ex) numpy와 pytorch는 굉장히 비슷한 방식의 코딩스타일을 갖는다.
(자료형의 경우에도 torch.uint8과 같이 표기가 가능)
import numpy as np
x = np.array([[1,2], [3,4]])

import torch
x = torch.Tensor([[1,2], [3,4]])

cf) torch.Tensor는 default로 float32를 갖는다.

 

 

2. Autograd

Autograd는 자동으로 미분 및 역전파를 수행하는 기능이다.
즉, 대부분의 tensor간의 연산을 크게 신경 쓸 필요 없이 역전파알고리즘 수행 명령어를 호출할 수 있다.

이때, tensor간의 연산을 수행할 때마다 동적으로 computational graph를 생성하며,
연산의 결과물이 어떤 tensor로부터 어떤 연산을 통해 왔는지 또한 추적한다.
→ 결과적으로 최종 스칼라값에 역전파를 통한 미분을 수행 시, 각 tensor는 자식노드에 해당하는 tensor와 연산을 자동으로 찾아 역전파알고리즘을 계속 수행할 수 있게한다.


🤔 기존 keras 및 tensorflow와 다른점??
keras tensorflow 미리 정의한 연산들을 컴파일을 통해 고정, 정해진 입력에 맞춰 tensor를 순전파 시켜야 한다.

반면, Pytorch 정해진 연산이 없고 모델은 배워야 하는 parameter tensor만 미리 알고있다
즉, 가중치들이 어떠한 연산을 통해 학습 or 연산에 관여하는지 알 수 없고, 연산이 수행된 직 후 알 수 있다.

기울기를 구할 필요가 없는 연산의 경우, 다음과 같은 with 문법을 사용해 연산을 수행할 수 있는데, 이는 prediction과 inference등을 수행할 때 유용하며, 기울기를 구하기 위한 computational graph 생성 등의 사전작업을 생략하여 연산속도 및 메모리 사용측면에서도 큰 이점이 존재한다.
with torch.no_grad():
	z = (x+y) + torch.Tensor(2,2)​

 

 

3.  nn.Module

nn.Module 클래스는 사용자가 그 위에서 필요한 모델 구조를 구현할 수 있게 해준다.
nn.Module을 상속한 사용자클래스는 다시 내부에 nn.Module을 상속한 클래스객체를 선언 및 변수로 사용할 수 있다.

ex) Feed Forward 구현
import torch
import torch.nn as nn

def linear(x, W, b):
	return torch.mm(W, x) + b

class MyLinear(nn.Module):
	def __init__(self, input_size, output_size):
    	super().__init__()
        	self.W = torch.FloatTensor(input_size, output_size)
            self.b = torch.FloatTensor(output_size)
   
   	def forward(self, x):
    	y = torch.mm(self.W, x) + self.b
        return y
        
        
        
x = torch.Tensor(16, 10)
linear = MyLinear(10, 5)
y = linear(x)​
x ∈ R16×10
W ∈ R 10×5
b ∈ R5

>>> print([p.size() for p in linear.parameters()])
>>> [ ]​
다만, 현재 parameter(W, b)의 경우, [ ]로 학습가능한 파라미터가 없다고 출력된다.
따라서 Parameter 클래스를 사용해 tensor를 감싸야 한다.

import torch
import torch.nn as nn

def linear(x, W, b):
	return torch.mm(W, x) + b

class MyLinear(nn.Module):
	def __init__(self, input_size, output_size):
    	super().__init__()
        	self.W = nn.Parameter(torch.FloatTensor(input_size, output_size), requires_grad=True)
            self.b = nn.Parameter(torch.FloatTensor(output_size), requires_grad=True)
   
   	def forward(self, x):
    	y = torch.mm(self.W, x) + self.b
        return y
        
        
        
x = torch.Tensor(16, 10)
linear = MyLinear(10, 5)
y = linear(x)​​

위의 경우, 출력값으로 [torch.Size([10, 5]), torch.Size([5])] 가 출력된다.

 

 

4.  train()과 eval()

Backpropagation Algorithm의 경우, backward()함수를 이용해 진행가능하며, 이때 loss함수를 앞에 붙이는 형태로 표현한다.
loss.backward()​



train과 eval함수는 모델에 대한 training time과 inference time의 모드를 쉽게 전환할 수 있는 방법이다.
nn.Module을 상속받아 구현∙생성된 객체는 기본적으로 train모드로 이를 eval()을 사용해 추론모드로 바꿀 수 있다.
이는 Dropout, Batch Norm 같은 학습시와 추론 시 서로 다른 forward()동작을 하는 모듈들에도 올바르게 동작할 수 있다.
다만, 추론이 끝나면 다시 train()을 선언 해 원래의 train모드로 돌아가줘야 한다.

 

 

5.  GPU 사용하기

cuda() 함수
① 원하는 tensor객체를 GPU메모리에 복사하거나 
② nn.Module의 하위클래스를 GPU메모리로 이동시킬 수 있다.
x = torch.cuda.FloatTensor(16, 10)

linear = MyLinear(10, 5)
linear.cuda()

y = linear(x)​


cpu() 함수
다시 PC의 메모리로 복사하거나 이동시킬 수 있다.

to() 함수
tensor 또는 모듈을 원하는 device로 보낼 수 있다.

 

 

 

 

6.  Pytorch에서 DNN을 수행하는 과정 요약 및 예시

 nn.Module 클래스를 상속받아 forward함수를 통해 모델구조 선언
 해당 클래스 객체 생성
 Optimizer 생성, 생성한 모델의 parameter를 최적화대상으로 등록
 Data로 mini-batch를 구성, 순전파 연산그래프 생성
 손실함수를 통해 최종결과값, 손실값 계산
 손실에 대해서 backward() 호출
  → 이를통해 연산그래프 상의 tensor들의 gradient가 채워짐
 ③의 optimizer에서 step()을 호출, 1 step 수행
 ④로 돌아가 반복

 

 

 

 

 

 

 

7.  Pytorch 실습

[Linear Regression 분석]

📌 조건

∙ 임의로 tensor를 생성, 근사하고자하는 정답함수(wx+b)에 넣어 정답(y)을 구함

∙ 신경망 통과한 y_hat과의 차이를 구함(이때, 오류함수는 MSE Loss function을 사용)

∙ SGD를 이용해 optimization 진행

 

 

❗️ 1개의 Linear Layer를 갖는 MyModel이라는 모듈 선언

import random
import torch
import torch.nn as nn

class MyModel(nn.Module):
	def __init__(self, input_size, output_size):
    	super(MyModel, self).__init__()
        self.linear = nn.Linear(input_size, output_size)
        
    def forward(self, x):
    	y = self.linear(x)
        return y

 

 

 

❗️ Wx + b가 3x1 + x2 - 2x3에 근사한다 가정하자.

def ground_truth(x):
	return 3*x[:,0] + x[:,1] - 2*x[:,2]

 

 

 

❗️ Hyper-Parameter 설정

batch_size = 1
epoch = 1000
iter = 10000

model = Mymodel(3, 1)
optim = torch.optim.SGD(model.parameters(), lr=0.0001, momentum=0.1)

 

 

 

❗️ Model과 tensor를 입력받아 순전파 후, 역전파알고리즘을 수행해 경사하강법 1 step을 진행

def train(model, x, y, optim):
	# 모듈의 모든 파라미터의 기울기 초기화
    optim.zero_grad()
    
    # Feed-Forward
    y_hat = model(x)
    
    # MSE Loss
    loss = ((y-y_hat)**2).sum() / x.size(0)
    
    # BP Algorithm
    loss.backward()
    
    # GD 1 step
    optim.step()
    
    return loss.data

 

 

 

 

❗️ Train & Inference time

for epoch in range(epoch):
	# train 설정
	avg_loss = 0
    
    for i in range(iter):
    	x = torch.rand(batch_size, 3)
        y = ground_truth(x.data)
        
        loss = train(model, x, y, optim)
        
        avg_loss += loss
        avg_loss = avg_loss / iter
        
        
        
    # valid 설정
    x_val = torch.FloatTensor([[.3, .2, .1]])
    y_val = ground_truth(x_val.data)
    
    
    
    # inference 
    model.eval()
    y_hat = model(x_val)
    model.train()
    
    print(avg_loss, y_val.data[0], y_hat.data[0, 0])
    
    
    # finish
    if avg_loss < .001:
    	break

 

'Gain Study' 카테고리의 다른 글

[Gain Study]. inference time, Mobilenet, 배포  (0) 2023.08.19
def scaled_dot_product_attention(Q, K, V):
  dim_k = Q.size(-1)
  scores = torch.bmm(Q, K.transpose(1, 2)) / sqrt(dim_k)
  weights = F.softmax(scores, dim=-1)
  return torch.bmm(weights, V)

 

from transformers import AutoConfig
from transformers import AutoTokenizer
import torch.nn.functional as F

model = "distilbert-base-uncased"
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
tokenizer = AutoTokenizer.from_pretrained(model).to(device)

inputs = tokenizer(text, return_tensors='pt', add_special_tokens=False)
config = AutoConfig.from_pretrained(model)

 

class Positional_Embedding(nn.Module):
  def __init__(self, config):
    super().__init__()
    self.token_embeddings = nn.Embedding(config.vocab_size, config.hidden_size)
    self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size)
    self.layer_norm = nn.LayerNprm(config.hidden_size, eps=1e-12)
    self.dropout = nn.Dropout()

  def forward(self, input_ids):
    seq_len = input_ids.size(1)
    position_ids = torch.arange(seq_len, dtype=torch.long).unsqueeze(0)
    token_embeddings = self.token_embeddings(input_ids)
    position_embeddings = self.position_embeddings(position_ids)

    embeddings = token_embeddings + position_embeddings
    embeddings = self.layer_norm(embeddings)
    embeddings = self.dropout(embeddings)
    return embeddings​

 

class Attention_Head(nn.Module):
  def __init__(self, embed_dim, head_dim):
    super().__init__()
    self.q = nn.Linear(embed_dim, head_dim)
    self.k = nn.Linear(embed_dim, head_dim)
    self.v = nn.Linear(embed_dim, head_dim)

  def forward(self, hidden_state):
    attention_outputs = scaled_dot_product_attention(
        self.q(hidden_state), self.k(hidden_state), self.v(hidden_state))
    return attention_outputs



class Multi_Head_Attention(nn.Module):
  def __init__(self, config):
    super().__init__()
    embed_dim = config.hidden_size
    head_num = config.num_attention_heads
    head_dim = embed_dim // head_num

    self.heads = nn.ModuleList(
        [Attention_Head(embed_dim, head_dim) for _ in range(head_num)]
    )
    self.output_linear = nn.Linear(embed_dim, dembed_dim)

  def forward(self, hidden_state):
    x = torch.cat([h(hidden_state) for h in self.heads], dim=-1)
    x = self.output_linear(x)
    return x

 

class FeedForward(nn.Module):
  def __init__(self, config):
    super().__init__()
    self.linear_1 = nn.Linear(config.hidden_size, config.intermediate_size)
    self.linear_2 = nn.Linear(config.intermediate_size, config.hidden_size)
    self.gelu = nn.GELU()
    self.dropout = nn.Dropout(config.hidden_dropout_prob)

  def forward(self, x):
    x = self.linear_1(x)
    x = self.gelu(x)
    x = self.linear_2(x)
    x = self.dropout(x)
    return x

 

class Transformer_Encoder(nn.Module):
  def __init__(self, config):
    super().__init__()
    self.embeddings = Embeddings(config)
    self.layers = nn.ModuleList([TransformerEncoder(config)
                                for _ in range(config.num_hidden_layers)])
    self.layer_norm_1 = nn.LayerNorm(config.hidden_size)
    self.layer_norm_2 = nn.LayerNorm(config.hidden_size)
    self.attetion = Multi_Head_Attention(config)
    self.feed_forward = FeedForward(config)

  def forward(self, x):
    x = self.embeddings(x)
    for layer in self.Layers:
      x = Layer(x)
    hidden_state = self.layer_norm_1(x)
    x = x + self.attention(hidden_state)
    x = x + self.feed_forward(self.layer_norm_2(x))
    return x

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 What is different between Pytorch and Tensorflow?

Pytorch Tensorflow(keras) 생성 / 구현
nn.Linear keras.layers.Dense Dense
nn.Module keras.layers.Layer 모델구성 기반클래스
nn.Dropout keras.layers.Dropout Dropout
nn.LayerNorm keras.layers.LayerNormalization Layer Normalization
nn.Embedding keras.layers.Embedding Embedding
nn.GELU keras.activations.gelu GELU 활성화 함수
nn.Bmm tf.matmul Batch 행렬곱셈
model.forward model.call 모델 정방향 패스

 

 

모델 정의 방법

tensorflow.keras

tf.debugging.set_log_device_placement(True)

class MyModel(tf.keras.Model):
    def __init__(self):
        super(MyModel,self).__init__()
        
        self.flatten = tf.keras.layers.Flatten(input_shape=(28,28))
        self.fc1 = tf.keras.layers.Dense(512,activation='relu')
        self.dropout = tf.keras.layers.Dropout(0.2)
        self.fc2 = tf.keras.layers.Dense(10, activation='softmax')
        
    def call(self,inputs):
        x = self.flatten(inputs)
        x = self.fc1(x)
        x = self.dropout(x)
        x = self.fc2(x)
        return x
        
model = MyModel()

cf) tensorflow는 tf.debugging.set_log_device_placement(True) 코드를 통해서 어디에 할당되어있는지를 확인할 수 있다.


PyTorch

# Get cpu or gpu device for training.
device = "cuda" if torch.cuda.is_available() else "cpu"

# Define model
class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )

    def forward(self, inputs):
        x = self.flatten(inputs)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)


모델 정의 시 차이점을 비교해 보자.

  GPU 할당 상속 클래스 fully connected layer 순방향 계산 함수명 모델 인스턴스 할당
PyTorch cuda.is_available() nn.Module nn.Linear call() model.to(device)
Tensorflow 자동 keras.Model keras.layers.Dense forward() model()



PyTorch는 GPU 탑재 여부를 담은 변수 device를 모델 객체에 할당할 때 포함시킵니다.
model = NeuralNetwork().to(device)


모델 컴파일 방법

tensorflow.keras

  • tensorflow는 정의한 모델 객체의 complile 메서드를 통해서 optimizer , loss function , metrics를 정의.
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])


PyTorch

  • PyTorch는 optimizer , loss function 변수에 저장
  • 저장된 변수를 학습 함수에서 직접 사용.
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)





모델 학습 방법

차이점은 모델 학습 부분에서 가장 큰 차이를 보인다.

tensorflow.keras

  • tensorflow 는 fit() 함수로 간단하게 학습을 사용할 수 있다.
model.fit(train_images, train_labels, epochs=5)


PyTorch

  • PyTorch는 함수 속 for 문을 정의하여 모델 학습(train)과 검증(test) 코드를 구현해야 합니다.
    PyTorch가 구현하기 어렵지만 더욱더 직관적이고 학습하기 용이하다.
  • optimizer.zero_grad() : Gradient를 초기화합니다.
    loss.backward() : Loss를 기준으로 Gradient를 저장합니다.
    optimizer.step() : 파라미터(weight) 업데이트.
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)

        # Compute prediction error
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
         
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
    
epochs = 5
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model, loss_fn)
print("Done!")




 

 

Ex) CIFAR100 with Pytorch

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

# Device 설정
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# CIFAR-100 데이터셋 불러오기
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

trainset = torchvision.datasets.CIFAR100(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR100(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=100, shuffle=False, num_workers=2)

# 신경망 모델 정의
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.relu1 = nn.ReLU()
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.relu2 = nn.ReLU()
        self.fc1 = nn.Linear(64 * 8 * 8, 512)
        self.relu3 = nn.ReLU()
        self.fc2 = nn.Linear(512, 100)
    
    def forward(self, x):
        x = self.pool(self.relu1(self.conv1(x)))
        x = self.pool(self.relu2(self.conv2(x)))
        x = x.view(-1, 64 * 8 * 8)
        x = self.relu3(self.fc1(x))
        x = self.fc2(x)
        return x

model = CNN().to(device)

# 손실 함수 및 최적화 알고리즘 정의
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

# 모델 학습
num_epochs = 100

for epoch in range(num_epochs):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data[0].to(device), data[1].to(device)
        optimizer.zero_grad()
        
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        
        running_loss += loss.item()
        if i % 100 == 99:
            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(trainloader)}], Loss: {running_loss/100:.4f}')
            running_loss = 0.0

print("Training finished!")

# 테스트 데이터셋을 사용하여 모델 평가
correct = 0
total = 0

with torch.no_grad():
    for data in testloader:
        images, labels = data[0].to(device), data[1].to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

accuracy = 100 * correct / total
print(f'Accuracy on test set: {accuracy:.2f}%')

 

 

 

 

+ Recent posts