본문 바로가기
경제학코딩 2023

[torch.nn-(2)]

by 개발도사(진) 2023. 12. 15.

Training

앞서 모델링에서 세운 model_1을 통해 실제로 training을 하는 코드를 구성해 본다.

먼저 training에 사용될 변수 epochs, batch_size, lr 을 선언한다. 

epochs = 1000 
batch_size = 128
lr = 0.01

epochs: 원래는 주어진 data 전체를 iteration하는 것을 epoch이라고 하나, 여기서는 랜덤하게 뽑은 특정 값 만큼 iteration을 수행할 것이다. 즉 반복할 횟수라고 이해하면 된다.

batch_size: random number를 몇 개 뽑을 것인지 지정해 주는 변수이다.

lr: learning rate. parameter update 속도를 지정하는 변수이다. 

 

이제 x_train(60000개 data) 중 batch_size만큼의 random number를 뽑아 예측값을 구하고, 그 예측값을 실제 값과 비교하여 loss를 산출하는 과정을 epoch만큼 반복 수행한다.

for epoch in range(epochs):
    idx = torch.randint(len(x_train), (batch_size,)) 
    xs = x_train[idx]
    ys = y_train[idx]

    prob = model_1(xs) 
    loss = loss_func(prob, ys) 
    if epoch % 100 == 0:
        print(loss)

    loss.backward()

 

이 뒤로는 learning rate값을 이용해서 parameter 값들을 조정하고 gradient 값을 초기화한다. 이 때 사용되는 값들에 대해서는 또 gradient를 구할 필요가 없기에 with torch.no_grad()를 사용한다.

    with torch.no_grad():        
        weights -= weights.grad * lr 
        bias -= bias.grad * lr
        
        weights.grad.zero_()
        bias.grad.zero_()

따라서 전체 코드는 아래와 같다.

epochs = 1000 
batch_size = 128
lr = 0.01

for epoch in range(epochs):
    idx = torch.randint(len(x_train), (batch_size,)) #전체 60000개 중 랜덤으로 128개씩 뽑음.
    xs = x_train[idx]
    ys = y_train[idx]

    prob = model_1(xs) #예측값 산출
    loss = loss_func(prob, ys) #실제 값(ys)와 비교해서 loss 산출
    if epoch % 100 == 0:
        print(loss)

    loss.backward() #backward propagation

    with torch.no_grad(): #이 문 안에 있는 코드들에 대해서는 gradient 구할 필요 없음
        #weight, bias 조정
        weights -= weights.grad * lr #weight값 조정 : gradient+learing rate
        bias -= bias.grad * lr

        #gradient 값 초기화
        weights.grad.zero_()
        bias.grad.zero_()

위 코드를 수행하면, 아래와 같은 결과를 얻을 수 있다.

 

tensor(x, y) 형태의 출력 결과에서 x가 나타내는 값이 loss이다. 즉, 반복할수록 loss 값이 감소하는 것을 확인할 수 있다.. 

앞서 세운 모델을 loss_func과 accuracy 함수에 넣고 실행한 결과이다. loss가 1.8177 이라는 결과는 직관적으로 model의 성공률을 받아들이기 쉽지 않지만, accuracy 함수의 결과값을 통해 이 모델의 정확도가 약 66% 임을 알 수 있다. 

 

여기서 세운 training model이 성공적이라면 반복을 하면 할수록 그 정확도가 늘어날 것이다. 앞선 코드들을 다시 한 번 수행하면 

위와 같은 값을 얻을 수 있는데, loss 값, accuracy 모두 상승하였음을 확인할 수 있다.

 

torch.nn functional

 

지금까지는 nll 을 계산해 주는 함수도 직접 구현하였고 softmax, log_softmax 도 직접 구현하여 model_1을 구현하였지만, torch.nn.functional을 이용하여 이와 같은 수고를 크게 줄일 수 있다. torch.nn.functional을 이용하여 model_1과 같은 기능을 구현하는 과정을 통해 살펴본다.

 

1) nll 대체

model_1을 이용한 training 에서는

def nll(prob, target): #negative log likelihood
    return -prob[range(target.shape[0]), target].mean()
    
def model_1(inputs):
outputs = inputs @ weights + bias
return log_softmax(outputs)

loss_func = nll

 위와 같이 직접 nll(negative log likelihood)를 구하는 함수를 만들고 이를 loss_func으로 지정해 주었고, 수동으로 log_softmax 함수를 만들어 적용해 주었다. 

 그러나 torch.nn.functional의 기능을 사용하면 위와 같은 기능을 

import torch.nn.functional as F
loss_func = F.cross_entropy

와 같이 쉽게 선언할 수 있다. 또한, softmax 역시 수동으로 적용할 필요가 없어지기에 다음과 같이 linear regression 값만 이용해서 training을 진행할 수 있다.

def model_2(inputs):
    return inputs @ weights + bias

위 내용을 종합하여 만든 model_2를 이용한 training code는 아래와 같다.

batch_size = 128

lr = 0.01
epochs = 1000

for epoch in range(epochs):

    idx = torch.randint(len(x_train), (batch_size,))
    xs = x_train[idx]
    ys = y_train[idx]

    prob = model_2(xs)
    loss = loss_func(prob, ys)
    if epoch % 100 == 0:
        print(loss)
    loss.backward()

    with torch.no_grad():
        weights -= weights.grad * lr
        bias -= bias.grad * lr

        weights.grad.zero_()
        bias.grad.zero_()

위 코드를 수행하면, 위 코드가 model_1과 같은 기능을 수행하고 있다는 것을 확인할 수 있다. 

 

nn.Module

model_1,2 에서 다뤘던 기능을 nn.Module을 사용하여 Class로 만든다. 여태까지는 하나하나 weight, bias를 만들고 graident를 선언해 주었던 것들을 nn.Parameter를 이용해 보다 간략히 선언하고, training을 위한 함수 또한 Class 의 member로 구현한다.

 

from torch import nn
class MyModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.weights = nn.Parameter(torch.randn(784, 10)) 
        self.bias = nn.Parameter(torch.zeros(10))

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

위 MyModel classs의 객체를 만들고, 앞서 만든 training code에서 model_ function이 들어간 부분을 교체한다.

#model=MyModel()이 선언되어 있어야 함
prob = model(xs)

또한 직접 parameter를 갱신해 주었던 부분을 아래와 같이 model instance의 member로 접근하는 방식으로 바꾼다.

with torch.no_grad():
        for p in model.parameters(): #parementer - weight, bias
            p -= p.grad * lr
        model.zero_grad()

위 과정을 모두 적용한 전체 training code는 아래와 같다.

batch_size = 128

lr = 0.01
epochs = 1000

for epoch in range(epochs):

    idx = torch.randint(len(x_train), (batch_size,))
    xs = x_train[idx]
    ys = y_train[idx]

    prob = model(xs)
    loss = loss_func(prob, ys)
    if epoch % 100 == 0:
        print(loss)
    loss.backward()

    with torch.no_grad():
        for p in model.parameters(): #parementer - weight, bias
            p -= p.grad * lr
        model.zero_grad()

 

nn.Linear

위 코드에서 linear regression 부분을 다시 개량한다. 지난 포스팅에서 지금까지 우리는 linear regression을 위해 weight, bias를 직접 선언하고,  matrix multiplication 부분도 직접 구현하였다. nn.Linear의 기능을 통해 위 과정을 대폭 간소화할 수 있다.

def __init__(self):
        super().__init__()
        self.linear = nn.Linear(784, 10)

위 코드가 지금까지 weight, bias를 직접 선언하던 부분을 모두 대체한다. 

def forward(self, x):
        return self.linear(x)

위 코드가 matrix multiplication 부분을 대체한다. 

 

torch.optim

torch.optim의 기능을 이용하여 parameter update 부분을 간소화한다. torch.optim을 imort하고, Adam()을 이용해 다루고자 하는 parameter를 optimizer 변수에 할당한다.

from torch import optim
optimizer = optim.Adam(params=model.parameters())

이제 지금까지 다룬 training code의 parameter update 부분을 아래와 같이 개선할 수 있다. 주석 처리된 부분은 optimizer가 대체하는 원래 코드를 의미한다.

#for p in model.parameters():
        #    p -= p.grad * lr
        optimizer.step()
        
        # model.zero_grad()
        optimizer.zero_grad()

 

위 과정을 모두 적용한 최종 코드와 그 결과는 아래와 같다.

batch_size = 128

lr = 0.01
epochs = 1000
for epoch in range(epochs):

        idx = torch.randint(len(x_train), (batch_size,))
        xs = x_train[idx]
        ys = y_train[idx]

        prob = model(xs)
        loss = loss_func(prob, ys)
        if epoch % 100 == 0:
            print(loss)

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

처음에 구상하였던 training model과 같은 역할을 훨씬 간략한 코드로 수행하면서, 동시에 torch.nn에 내장된 기능들을 이용하면서 정확도 역시 상승하였음을 볼 수 있다.

'경제학코딩 2023' 카테고리의 다른 글

[Encoder Only Model with imdb]  (0) 2023.12.17
[Encoder Only Model with Random Data]  (1) 2023.12.17
[Keras Layer - (2)]  (0) 2023.12.16
[Keras Layer-(1)]  (0) 2023.12.16
[torch.nn - (1)]  (0) 2023.12.14