Pytorch로 신경망 구축하기

이신행·2025년 2월 6일
0

신경망의 경우, 데이터에 대한 연산을 수행하는 계층(layer)/모듈(module)로 구성되어 있다.

torch.nn은 신경망을 구성하는데 필요한 요소를 제공한다. Pytorch의 모든 모듈은 nn.Module의 하위 클래스이다. 신경망은 다른 모듈로 구성된 모듈인데, 이러한 중첩된 구조는 복잡한 아키텍처를 쉽게 구축하고 관리할 수 있다.

또한, 신경망 학습을 위해 GPU or MPS같은 옵션을 설정할 수 있는데, 이를 설정하지 않으면, 기본적으로 CPU가 사용된다.

MPS의 경우는 주로 MAC OS에서 사용된다.

import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

device = (
    "cuda"
    if torch.cuda.is_available()
    else "mps"
    if torch.backends.mps.is_available()
    else "cpu"
)
print(f"Using {device} device")

클래스 정의

신경망 모델을 nn.Module의 하위 클래스로 정의, __init__에서 신경망 계층들을 초기화한다. nn.Module을 상속받은 모든 클래스는 forward 메소드에 입력 데이터에 대한 연산들을 구한다.

class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__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, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

학습 진행 전, NeuralNetwork의 인스턴스를 생성하고, 이를 device(cuda, cpu, mps)로 이동한 뒤, 구조를 출력한다.

model = NeuralNetwork().to(device)
print(model)
--------------------------------------
NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
  )
)

모델을 사용하기 위해 입력을 전달하는데, 이는 일부 백그라운드 연산들과 함께 모델의 forward를 수행한다. (model.forward()를 직접 호출하지 않는다!)

모델에 입력을 전달해 호출하면 2차원 텐서를 반환하는데, 2차원 텐서의 dim=0은 각 클래스에 대한 raw 예측값 10개가, dim=1에는 각 출력의 개별 값들이 해당한다. raw 예측값을 nn.Softmax 모듈의 인스턴스에 통과시켜 예측 확률을 얻는다.

X = torch.rand(1, 28, 28, device=device)
logits = model(X)
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1)
print(f"Predicted class: {y_pred}")
-----------------------------------------
Predicted class: tensor([7], device='cuda:0')

모델 계층(layer)

FashionMNIST 모델의 계층을 살펴본다면, 28x28 크기의 이미지 3개로 구성된 미니배치로, 신경망을 통과할 때를 확인한다.

input_image = torch.rand(3,28,28)
print(input_image.size())
----------------------------------
torch.Size([3, 28, 28])

nn.Flatten

nn.Flatten은 평탄화된 배열로 전환하는 역할을 수행하는 함수이다.

nn.Flatten을 적용해 784 픽셀을 갖는 연속된 배열로 반환한다.

flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())
------------------------------------
torch.Size([3, 784])

nn.Linear

nn.Linear(선형 계층)을 통해 저장된 weight, bias를 사용해 입력에 선형 변환을 적용한다.

layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())
------------------------------------
torch.Size([3, 20])

nn.ReLU(Activation Function)을 적용해 모델의 입,출력 사이에 복잡한 관계를 만든다. 비선형 활성화의 경우, 선형 변환 후에 주로 적용되며, 비선형성을 도입하고 신경망이 다양한 현상을 학습할 수 있도록 돕는다.

실제는 ReLU, 하이퍼볼릭탄젠트, LeakyReLU 등 다양한 옵션을 사용할 수 있다.

print(f"Before ReLU: {hidden1}\n\n")
hidden1 = nn.ReLU()(hidden1)
print(f"After ReLU: {hidden1}")
----------------------------------
Before ReLU: tensor([[-0.0104,  0.0140, -0.1345, -0.2063, -0.0457,  0.1683,  0.8263, -0.0393,
          0.1695, -0.1549, -0.2835,  0.5268,  0.1961, -0.1832,  0.1592,  0.1190,
         -0.0760,  0.0419,  0.2624,  0.2088],
        [-0.2475, -0.2524,  0.1190, -0.4282, -0.0164,  0.2265,  0.4653, -0.0714,
          0.2430,  0.1137,  0.4633,  0.5006,  0.1300,  0.0761,  0.1044,  0.3631,
         -0.3045, -0.2601,  0.6209, -0.1073],
        [-0.2800, -0.4316,  0.0410,  0.1125, -0.0112,  0.2403,  0.3667,  0.1970,
          0.2418, -0.2051, -0.0914,  0.5844,  0.1634,  0.0156, -0.2390,  0.3897,
         -0.0655, -0.0901,  0.5191, -0.3114]], grad_fn=<AddmmBackward0>)

After ReLU: tensor([[0.0000, 0.0140, 0.0000, 0.0000, 0.0000, 0.1683, 0.8263, 0.0000, 0.1695,
         0.0000, 0.0000, 0.5268, 0.1961, 0.0000, 0.1592, 0.1190, 0.0000, 0.0419,
         0.2624, 0.2088],
        [0.0000, 0.0000, 0.1190, 0.0000, 0.0000, 0.2265, 0.4653, 0.0000, 0.2430,
         0.1137, 0.4633, 0.5006, 0.1300, 0.0761, 0.1044, 0.3631, 0.0000, 0.0000,
         0.6209, 0.0000],
        [0.0000, 0.0000, 0.0410, 0.1125, 0.0000, 0.2403, 0.3667, 0.1970, 0.2418,
         0.0000, 0.0000, 0.5844, 0.1634, 0.0156, 0.0000, 0.3897, 0.0000, 0.0000,

nn.Sequential

순서를 갖는 모듈의 컨테이너를 말하고, 순서대로 수행하게끔 유도한다. Sequential에서 정의된 것과 같은 순서로 모든 모듈을 통해 전달된다. Sequential Container을 통해 신경망을 빠르게 구축할 수 있다.

seq_modules = nn.Sequential(
    flatten,
    layer1,
    nn.ReLU(),
    nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image)

nn.Softmax

마지막 선형 계층은 nn.Softmax 모듈에 전달될 ([-infty, infty] 범위의 원시 값(raw value)인) logits 를 반환한다. logits는 모델의 각 클래스에 대한 예측 확률을 나타내도록 [0, 1] 범위로 비례하여 조정되고, dim 매개변수는 값의 합이 1이 되는 차원을 나타냅니다.

softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)

Parameter

nn.Module을 상속하면 모델 객체 내부의 모든 필드들이 자동으로 추적되며, parameters(), named_parameters() 메소드로 모든 매개변수에 접근할 수 있게 된다.

해당 예제에서는 각 매개변수들을 순회하며 매개변수의 크기와 값을 출력한다.

print(f"Model structure: {model}\n\n")

for name, param in model.named_parameters():
    print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
    
--------------------------------------------
Model structure: NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
  )
)

Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[ 0.0262,  0.0165, -0.0289,  ..., -0.0029,  0.0231,  0.0286],
        [-0.0257,  0.0352,  0.0302,  ...,  0.0138,  0.0338, -0.0297]],
       device='cuda:0', grad_fn=<SliceBackward0>)

Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([0.0047, 0.0353], device='cuda:0', grad_fn=<SliceBackward0>)
profile
AI 개발자에서 이제는 대학원생

0개의 댓글