본문 바로가기
AI

[파이토치]신경망 구성

by Reodreamer 2022. 9. 29.
반응형

이번 포스트에서는 본격적으로 우리가 흔히 모델로 알고 있는 신경망을 구축하는것을 알아보자. 아직 텐서변환에 대해 공부하지 않은 분들은 이전의 포스트를 먼저 보고 오면 도움이 될것이라고 생각한다. 

 

[파이토치] Transform

이번 포스트는 파이토치에서 학습을 위해 데이터를 처리의 한 부분인 Transform을 하는지 공부해 보자. Transform은 왜 필요하고 어떻게 할까? 머신러닝과 딥러닝에서 데이터를 학습에 용이하게 활용

dream-be.tistory.com


신경망은 데이터의 연산을 담당하는 레이어와 모듈로 이루어져 있다. torch.nn은 신경망을 구축하는데 필요한 모든 구성요소들을 제공한다. 파이토치가 제공하믄 모든 모듈은 nn.Module의 하위 클래스이다. 신경망은 그 자체가 다른 레이어와 모듈로로 이루어진 모듈이다. 이렇게 중첩되는 구조가 복작한 구조를 설계하고 관리하는 것을 용이하게 한다. 

 

1. Class 정의 

신경망을 nn.Module의 하위클래스로 정의 하고 __init__을 통해 신경망 레이어들을 초기화 한다. 모든 nn.Module의 하위클래스는 데이터에 대해  forward 방식으로 연산을 수행한다. 

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),
            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로 옮기고 구조를 확인한다.

model = NeuralNetwork().to(device)
print(model)

Out:

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 함수를 실행한다. 여기서 주의 할점은 모델의 forward를 직접적으로 부르면 안된다는 것이다. 

 

X = torch.rand(1,28,28, device= device)
logits = model(X)
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1) # 1차원으로 줄인다. 
print(y_pred)

Out:

tensor([4], device='cuda:0')

2. 모델 레이어 

데이터가 신경망을 통과하면서 어떤일이 일어나는지 확인하기 위해, 사이즈가 28x28인 3장의 이미지로 이루어진 미니배치를 신경망에 통과 시켜보자. 

먼저 배치크기가 3이고 사이즈가 28x28 텐서를 만든다. 

input_image = torch.rand(3,28,28)
print(input_image.size())

Out:

torch.Size([32828])

2.1 nn.Flatten

nn.Flatten은 0번째 차원인 배치크기는 유지하되 나머지 다차원의 데이터를1차원으로 줄여주는 기능을 한다. 

flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())

Out:

torch.Size([3784])

보이는것과 같이 사이즈 28x28 2차원의 텐서가 flatten과정을 거쳐서 1차원의 784로 바뀐것을 확인할수 있다. 

 

2.2 nn.Linear

Linear 레이어는 입려되는 데이터에 저장된 가중치와 편향을 적용하여 선형변환을 적용하는 모듈이다.

layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())

Out:

torch.Size([320])

2.3 nn.ReLU

nn.ReLU는 우리가 잘 알고있는 ReLU 함수를 적용하는것을 의미한다. 신경망에서 입력데이터가 저장된 가중치와 편향의 영향을 받고 그 값들이 저장된 hidden layer의 값에 비선형성을 적용하는것이다. 비선형성이 추가되면 입력과 출력 사이에 복잡한 매핑이 가능하게 되고 신경망의 깊이의 층을 깊이 하는것을 유의미 하게 한다. 

print(f"Before ReLU: {hidden1[1]}\n\n")
hidden1 = nn.ReLU()(hidden1[1])
print(f"After ReLU: {hidden1}")

Out:

Before ReLU: tensor([0.08460.01450.32890.00000.08060.00000.00000.33540.0025,
        0.45260.31560.08350.23970.00000.10890.00000.12340.0062,
        0.00000.0000], grad_fn=<SelectBackward0>)


After ReLU: tensor([0.08460.01450.32890.00000.08060.00000.00000.33540.0025,
        0.45260.31560.08350.23970.00000.10890.00000.12340.0062,
        0.00000.0000], grad_fn=<ReluBackward0>)

2.4 nn.sequential

nn.sequential는 우선 컨테이너라고 생각하면 이해하기 쉽다. 그렇다면 이 안에 무엇을 담을까? 모듈들을 순서대로 담는다. 그렇게 함으로서 입력 데이터를 순차적으로 처리할 수 있다.

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

 2.5 nn.Softmax

 신경망의 마지막 선형 레이어로서 범위가 [-inf, inf] 데이터를 raw value인 logit을 변환한다. 이렇게 변환하는것은 모델이 각 클래스에 대한 예측확률을 [0,1]의 범위에서 스케일링 한다. 이후 logit은 nn.Softmax를 거쳐 각 요소별로 확률값을 반환한다. 아래에서 logit이 softmax를 거친 후에 어떻게 바뀌는지 확인해보자. 

 

logits:

tensor([[-0.0789,  0.0106-0.2217-0.0920-0.1328-0.2466-0.1793-0.1878,
          0.2070-0.1848],
        [-0.1232-0.0030-0.1300-0.0737-0.3079-0.1493-0.1877-0.3093,
          0.2709-0.3222],
        [-0.0279-0.0216-0.1525-0.0757-0.2545-0.1439-0.1674-0.1996,
          0.3437-0.2028]], grad_fn=<AddmmBackward0>)

softmax 적용 

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

Out:

tensor([[0.10230.11190.08870.10100.09700.08650.09250.09180.1362,
         0.0920],
        [0.09950.11220.09890.10460.08270.09700.09330.08260.1476,
         0.0816],
        [0.10490.10560.09260.10000.08360.09340.09130.08840.1521,
         0.0881]], grad_fn=<SoftmaxBackward0>)

softmax를 적용한 후의 텐서내 요소들의 값을 확인해보면 모든 값들이 0~1 범위로 변환된 것을 확인할 수 있다. 

 

3. 모델 파라미터 

신경망의 많은 레이어들은 파라미터화 된다. nn.Module의 하위 클래스를 만드는 것은 자동적으로 모델 객체내에서 정의한 모든 필드를 추적하고, parameter()와, 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")

Out:

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([512784]) | Values : tensor([[ 0.0348,  0.0080-0.0126,  ..., -0.0051-0.0307-0.0030],
        [-0.0130,  0.0344-0.0065,  ...,  0.0207,  0.0073,  0.0007]],
       device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([ 0.0116-0.0144], device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.2.weight | Size: torch.Size([512512]) | Values : tensor([[ 0.0285-0.0114-0.0349,  ..., -0.0027-0.0193-0.0046],
        [-0.0354-0.0214,  0.0112,  ...,  0.0347-0.0022-0.0050]],
       device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : tensor([-0.0083-0.0247], device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.4.weight | Size: torch.Size([10512]) | Values : tensor([[-0.0060,  0.0218,  0.0335,  ..., -0.0395-0.0214,  0.0338],
        [ 0.0045,  0.0115-0.0334,  ..., -0.0035-0.0217-0.0058]],
       device='cuda:0', grad_fn=<SliceBackward0>) 

Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : tensor([ 0.0110-0.0350], device='cuda:0', grad_fn=<SliceBackward0>) 

이번 포스트에서는 딥러닝의 핵심인 신경망을 구성하는 것에 대해 공부했다. 다음은 모델이 학습을 하면서 기울기를 자동으로 미분해주는 AUTOGRAD에 대해 알아볼 것이다.

반응형

'AI' 카테고리의 다른 글

[파이토치]최적화  (1) 2022.10.06
[파이토치]자동미분_AUTOGRAD  (0) 2022.09.30
[논문리뷰]Conditional Generative Adversarial Nets(CGAN)  (1) 2022.09.23
[파이토치] Transform  (1) 2022.09.21
[논문리뷰]Attention Is All You Need  (1) 2022.09.20

댓글