자바스크립트가 비활성화 되어있습니다.
자바스크립트가 활성화 되어야 콘텐츠가 깨지지 않고 보이게 됩니다.
자바스크립트를 사용할수 있도록 옵션을 변경해 주세요.
- willbsoon

본문 바로가기
Bigdata, AI/NLP

예제로 배우는 파이토치3. 마지막.

by willbsoon 2020. 8. 25.

파이토치 튜토리얼 3번째~!!!

이 글은 파이토치 공식 튜토리얼에서 가져온 예제다. 더 정확한 내용을 보시려면 여기를 참조

 

사용자 정의 nn.Module

때때로 기존 모듈의 구성보다 더 복잡한 모델을 구성해야할 때가 있습니다. 이럴 때는 nn.Module의 서브클래스로 새 모듈을 정의하고 입력 tnesor를 받아 다른 모듈 또는 tensor의 autograd 연산을 사용하여 출력 Tensor를 만드는 forward를 정의 해보자.

 

이전 포스팅에서 만들었던 모델은 nn.Sequential 모델이었다면 이제는 사용자정의 모델이다.

import torch


class TwoLayerNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        """
        생성자에서 2개의 nn.Linear 모듈을 생성하고, 멤버 변수로 지정합니다.
        """
        super(TwoLayerNet, self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H)
        self.linear2 = torch.nn.Linear(H, D_out)

    def forward(self, x):
        """
        순전파 함수에서는 입력 데이터의 Tensor를 받고 출력 데이터의 Tensor를
        반환해야 합니다. Tensor 상의 임의의 연산자뿐만 아니라 생성자에서 정의한
        Module도 사용할 수 있습니다.
        """
        h_relu = self.linear1(x).clamp(min=0)
        y_pred = self.linear2(h_relu)
        return y_pred


# N은 배치 크기이며, D_in은 입력의 차원입니다;
# H는 은닉층의 차원이며, D_out은 출력 차원입니다.
N, D_in, H, D_out = 64, 1000, 100, 10

# 입력과 출력을 저장하기 위해 무작위 값을 갖는 Tensor를 생성합니다.
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# 앞에서 정의한 클래스를 생성하여 모델을 구성합니다.
model = TwoLayerNet(D_in, H, D_out)

# 손실 함수와 Optimizer를 만듭니다. SGD 생성자에 model.parameters()를 호출하면
# 모델의 멤버인 2개의 nn.Linear 모듈의 학습 가능한 매개변수들이 포함됩니다.
criterion = torch.nn.MSELoss(reduction='sum')      # loss_fn?
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(500):
    # 순전파 단계: 모델에 x를 전달하여 예상되는 y 값을 계산합니다.
    y_pred = model(x)

    # 손실을 계산하고 출력합니다.
    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # 변화도를 0으로 만들고, 역전파 단계를 수행하고, 가중치를 갱신합니다.
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

옵티마이저를 넣으니깐 확실히 loss가 확 줄어들긴 한다. 딥러닝이 그만큼 발전하고 있다는 얘기겠지??

99 2.6078920364
199 0.0401864611
299 0.0009951270
399 0.0000280238
499 0.0000008421

 

 

자 이제 마지막이다. 흐름제어와 가중치 공유~!!!!

 

pytorch : 제어흐름(ControlFlow) + 가중치 공유(Weight sharing)

 동적 그래프와 가중치 공유의 예로, 매우 이상한 모델을 구현해보겠습니다. 각 순전파 단계에서 많은 은닉 계층을 갖는 완전히 연결된 ReLU 신경망이 무작위로 0~3 사이의 숫자를 선택하고 가장 안쪽의 은닉층들을 계산하기 위해 동일한 가중치를 여러번 재사용합니다.

 

이 모델에서는 일반적인 python 제어흐름을 사용하여 반복을 구현할 수 있으며 순전파 단계를 정의할때 단지 동일한 Module을 여러번 재사용함으로써 내부계층들 간의 가중치 공유를 구현할 수있음.

 

import random
import torch


class DynamicNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        """
        생성자에서 순전파 단계에서 사용할 3개의 nn.Linear 인스턴스를 생성합니다.
        """
        super(DynamicNet, self).__init__()
        self.input_linear = torch.nn.Linear(D_in, H)
        self.middle_linear = torch.nn.Linear(H, H)
        self.output_linear = torch.nn.Linear(H, D_out)

    def forward(self, x):
        """
        모델의 순전파 단계에서, 무작위로 0, 1, 2 또는 3 중에 하나를 선택하고
        은닉층을 계산하기 위해 여러번 사용한 middle_linear Module을 재사용합니다.

        각 순전파 단계는 동적 연산 그래프를 구성하기 때문에, 모델의 순전파 단계를
        정의할 때 반복문이나 조건문과 같은 일반적인 Python 제어 흐름 연산자를 사용할
        수 있습니다.

        여기에서 연산 그래프를 정의할 때 동일 Module을 여러번 재사용하는 것이
        완벽히 안전하다는 것을 알 수 있습니다. 이것이 각 Module을 한 번씩만 사용할
        수 있었던 Lua Torch보다 크게 개선된 부분입니다.
        """
        h_relu = self.input_linear(x).clamp(min=0)
        for _ in range(random.randint(0, 3)):
            h_relu = self.middle_linear(h_relu).clamp(min=0)
        y_pred = self.output_linear(h_relu)
        return y_pred


# N은 배치 크기이며, D_in은 입력의 차원입니다;
# H는 은닉층의 차원이며, D_out은 출력 차원입니다.
N, D_in, H, D_out = 64, 1000, 100, 10

# 입력과 출력을 저장하기 위해 무작위 값을 갖는 Tensor를 생성합니다.
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# 앞서 정의한 클래스를 생성(instantiating)하여 모델을 구성합니다.
model = DynamicNet(D_in, H, D_out)

# 손실함수와 Optimizer를 만듭니다. 이 이상한 모델을 순수한 확률적 경사 하강법
# (stochastic gradient decent)으로 학습하는 것은 어려우므로, 모멘텀(momentum)을
# 사용합니다.
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
for t in range(500):
    # 순전파 단계: 모델에 x를 전달하여 예상되는 y 값을 계산합니다.
    y_pred = model(x)

    # 손실을 계산하고 출력합니다.
    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # 변화도를 0으로 만들고, 역전파 단계를 수행하고, 가중치를 갱신합니다.
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

 

마지막 예제는 좀.. 억지로 만들어 놓은거라 결과도 좋지 못했지만

pytorch에 대한 이해는 조금 생긴것 같다. 기존의 머신러닝 이론과 파이토치를 통해 shallow neural network를 만들어보니 그나마 조금은 알것 같은 느낌이 든다. 눈으로 보는것 보단 실제 코딩해보는게 역시 좋은것 같다.

바쁘단 핑계로 대충대충 했는데 다시 열심히 해보자.

댓글