R에서 torch를 사용하는 방식은 torch 패키지와 rTorch 가 있다. 오늘은 그중에서도 rTorch패키지를 배워보도록 하겠다.

참고로 torch 패키지는 R에서 python을 불러오지 않아도 된다는 장점이 있다. 하지만 필자는 혹여나 버전문제가 걱정되어 Rtorch를 사용한다.

MNIST 이미지 자료를 분류하는 CNN 모형은 아래와 같이 생성할 수 있다.

패키지 로드

library(rTorch)
library(reticulate)

nn          <- torch$nn
transforms  <- torchvision$transforms
f<-torch$nn$functional
Optim=torch$optim
datasets=torchvision$datasets
transforms=torchvision$transforms
torch$set_default_dtype(torch$float)

파라미터 설정

seed=1L
batch_size = 64L
test_batch_size = 64L
no_cuda =F

use_cuda = !no_cuda & torch$cuda$is_available()
device   = torch$device(ifelse(use_cuda,'cuda','cpu'))

torch$manual_seed(seed)

데이터 로더 설정

train_loader=torch$utils$data$DataLoader(
  datasets$MNIST(
    'dataset',train=T,download=T,
    transform=transforms$Compose(
      list(
        transforms$ToTensor(),
        transforms$Normalize(
          0.1307,0.3081
        )
      )
    )),
    batch_size=batch_size,shuffle=T
)

test_loader=torch$utils$data$DataLoader(
  datasets$MNIST(
    'dataset',train=F,
    transform=transforms$Compose(
      list(
        transforms$ToTensor(),
        transforms$Normalize(
          0.1307,0.3081
        )
      )
    )),
  batch_size=test_batch_size,shuffle=T
)

모델링

모델링 방식은 2가지가 있다. py_run_string을 통해 파이썬 구문을 그대로 활용하는 방법과 reticulate 패키지의 PyClass를 이용하는 방법이 있다.

# 모델링 
py_run_string("import torch")
main = py_run_string(
  "
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
   def __init__(self):
       super(Net, self).__init__()
       self.conv1 = nn.Conv2d(1, 20, 5, 1)
       self.conv2 = nn.Conv2d(20, 50, 5, 1)
       self.fc1 = nn.Linear(4*4*50, 500)
       self.fc2 = nn.Linear(500, 10)
       
   def forward(self, x):
       x = F.relu(self.conv1(x))
       x = F.max_pool2d(x, 2, 2)
       x = F.relu(self.conv2(x))
       x = F.max_pool2d(x, 2, 2)
       x = x.view(-1, 4*4*50)
       x = F.relu(self.fc1(x))
       x = self.fc2(x)
       return F.log_softmax(x, dim=1)
")

model=main$Net()$to(device)


main=PyClass(classname ='Net',
  inherit =nn$Module,
  list(
  `__init__`=function(self){
    super()$`__init__`()
    self$conv1 = nn$Conv2d(1L,20L,5L,1L)
    self$conv2 = nn$Conv2d(20L,50L,5L,1L)
    self$fc1   = nn$Linear(4L*4L*50L, 500L)
    self$fc2   = nn$Linear(500L, 10L)
    NULL
  },
  forward=function(self,x){
    x=f$relu(self$conv1(x))
    x=f$max_pool2d(x,2L,2L)
    x=f$relu(self$conv2(x))
    x=f$max_pool2d(x,2L,2L)
    x=x$view(-1L,(4L*4L*50L))
    x=f$relu(self$fc1(x))
    x=self$fc2(x)
    return(f$log_softmax(x,dim=1L))
  })
)
model=main()$to(device)

옵티마이저 설정 및 계산과정 확인

optimizer=Optim$SGD(
  model$parameters(),
  lr=0.001,momentum=0.5)
model$train()

iter_train_loader <- iterate(train_loader)
iter_test_loader <- iterate(test_loader)
list2env({
  a=iter_train_loader[[1]];
  names(a)<-c('data','target');a},
  envir=.GlobalEnv)
dim(data);dim(target)
# 디바이스에 이동
data=data$to(device)
target=target$to(device)
# 그레디언트 초기화 
optimizer$zero_grad()
# 모델 출력값 
output=model(data)
# 손실함수 계산 negative Log-likelihood loss
loss=f$nll_loss(output,target)
# backpropagation 
loss$backward()
# parameter update
optimizer$step()

학습 및 검증


# 반복 
epochs=1
library(glue)
log_interval = 100
for(epoch in 1:epochs){
  model$train();batch_idx=0;n=length(iter_train_loader)
  for(obj in iter_train_loader){batch_idx=batch_idx+1
    list2env(
      {a=obj;names(a)<-c('data','target');a},
      envir=.GlobalEnv)
    data=data$to(device)
    target=target$to(device)
    optimizer$zero_grad()
    output = model(data)
    loss = f$nll_loss(output, target)
    loss$backward()
    optimizer$step()
    
    if(batch_idx%%log_interval==0){
      print(
        sprintf('Train Epoch: %i [%i/%i (%03f)] Loss: %06f',
                epoch,batch_idx*nrow(data),length(train_loader$dataset),
                100*batch_idx/length(train_loader),loss$item())
      )
    }
  }
  model$eval()
  
  test_loss=0
  correct = 0
  with(torch$no_grad(), {
    for(test_obj in iter_test_loader){
      list2env(
        {a=test_obj;names(a)<-c('data','target');a},
        envir=.GlobalEnv)
      data=data$to(device)
      target=target$to(device)
      output = model(data)
      test_loss =test_loss+f$nll_loss(output, target,reduce = 'sum')$item()
      pred = output$argmax(dim=1L, keepdim=T)
      correct=correct+pred$eq(target$view_as(pred))$sum()$item()
    }
  })
  test_loss= test_loss/length(test_loader$dataset)
  print(
    sprintf('Test set: Average Loss: {%.4f}, Accuracy: %i/%i ({%.0f})',
            test_loss, correct, length(test_loader$dataset),
            100 * correct / length(test_loader$dataset))
    )
}

참고

GitHub - f0nzie/rTorch
Contribute to f0nzie/rTorch development by creating an account on GitHub.
pytorch-tutorial/main.py at master · yunjey/pytorch-tutorial
PyTorch Tutorial for Deep Learning Researchers. Contribute to yunjey/pytorch-tutorial development by creating an account on GitHub.
Chapter 3 rTorch vs PyTorch | A Minimal rTorch Book
This is a minimal tutorial about using the rTorch package to have fun while doing machine learning. This book was written with bookdown.