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 |