{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Package setup"},{"metadata":{"trusted":true},"cell_type":"code","source":"install.packages(\"remotes\")\nremotes::install_github(\"mlverse/torchvision\", upgrade = FALSE)\nremotes::install_github(\"athospd/dogbreed\", upgrade = FALSE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"remotes::install_github(\"mlverse/torch\", upgrade = FALSE) # nightly","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"library(torch)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch_ones(2,3)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"051d70d956493feee0c6d64651c6a088724dca2a","_execution_state":"idle","trusted":true},"cell_type":"code","source":"library(tidyverse)\nlibrary(torch)\nlibrary(torchvision)\nlibrary(zeallot)\nlibrary(dogbreed)\n\n# config\nDATASET_FOLDER <- \"../input/\"\nNUM_WORKERS <- 0\nBATCH_SIZE <- 1\nNUM_EPOCHS <- 20\nDEVICE <- torch::torch_device(if(cuda_is_available()) \"cuda\" else \"cpu\")\nDEVICE <- torch::torch_device(\"cpu\")\nDEVICE","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_transform <- function(img) {\n   img <- img %>%\n     # first convert image to tensor\n     torchvision::transform_to_tensor() %>%\n     # data augmentation\n     torchvision::transform_random_affine(15, shear = c(-10, 10)) %>%\n     # data augmentation\n     torchvision::transform_random_resized_crop(size = c(224, 224)) %>%\n     # data augmentation\n     torchvision::transform_color_jitter() %>%\n     # data augmentation\n     torchvision::transform_random_horizontal_flip() %>%\n     # normalize according to what is expected by resnet\n     torchvision::transform_normalize(\n       mean = c(0.485, 0.456, 0.406),\n       std = c(0.229, 0.224, 0.225)\n     ) \n  img[..,1:224,1:224]\n}\n\nsubmission_transform <- function(img) {\n  img <- img %>%\n    torchvision::transform_to_tensor() %>%\n    torchvision::transform_resize(size = c(224, 224)) %>%\n    torchvision::transform_normalize(\n      mean = c(0.485, 0.456, 0.406), \n      std = c(0.229, 0.224, 0.225)\n    )\n  img[..,1:224,1:224]\n}\n\ntrain <- dog_breed_dataset(\n  root = DATASET_FOLDER, \n  download = TRUE,\n  split = \"train\", \n  loader = magick_loader\n)\nsubmission <- dog_breed_dataset(\n  root = DATASET_FOLDER, \n  download = TRUE,\n  split = \"submission\", \n  loader = base_loader, \n  transform = submission_transform\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":" sessionInfo()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_transform(train[1]$x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nmagick_loader(paste0(train$`.path`, \"/\", train[1]$id, \".jpg\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[1]$label\n\ntrain[1]$y\n\nDEVICE","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Dataloaders"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dl <- dataloader(\n  dataset = train, \n  batch_size = BATCH_SIZE, \n  shuffle = TRUE,\n  num_workers = NUM_WORKERS,\n  worker_globals = c(\"DEVICE\")\n)\n\nsubmission_dl <- dataloader(\n  dataset = submission, \n  batch_size = BATCH_SIZE, \n  shuffle = FALSE,\n  num_workers = NUM_WORKERS,\n  worker_globals = c(\"DEVICE\")\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model\n\nResnet18 with custom 100-output endpoint. Pretrained weigths are frozen."},{"metadata":{"trusted":true},"cell_type":"code","source":"model <- nn_dog(classes = 120, device = DEVICE)\nprint(model)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Training loop"},{"metadata":{"trusted":true},"cell_type":"code","source":"# model <- torch::torch_load(\"../inst/model\")\n# model$parameters$resnet18.fc.bias$requires_grad_(TRUE)\n# model$parameters$resnet18.fc.weight$requires_grad_(TRUE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"optimizer <- optim_sgd(model$parameters, lr = 0.0005, momentum = 0.9)\nscheduler <- lr_one_cycle(\n  optimizer, \n  max_lr = 0.05, \n  epochs = NUM_EPOCHS, \n  steps_per_epoch = length(train_dl)\n)\nloss_function <- nn_cross_entropy_loss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"aff <- enumerate(train_dl)\n\naff[[1]][[1]]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"num_epochs = NUM_EPOCHS\nfor(epoch in 1:num_epochs) {\n  # progress bar for feedback\n  pb <- set_progress_bar(length(train_dl))\n  pb$message(glue::glue(\"Epoch {epoch}/{num_epochs}\"))\n  \n  model$train()\n  losses <- rep(NA_real_, 5)\n  \n  for(b in enumerate(train_dl)) {\n     \n    optimizer$zero_grad()\n    output <- model(b[[1]]$to(device = DEVICE))\n    target <- b[[2]]$to(device = DEVICE)\n    loss <- loss_function(output, target)\n    loss$backward()\n    optimizer$step()\n    scheduler$step()\n    \n    losses <- c(loss$item(), losses)\n    pb$tick(tokens = list(loss = round(mean(losses[1:5], na.rm = TRUE), 4)))\n  }\n  \n  model$eval()\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch_save(model, \"../inst/model\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Evalutaion"},{"metadata":{"trusted":true},"cell_type":"code","source":"eval <- data.frame(output = NULL, target = NULL)\ncoro::loop(for(b in test_dl) {\n  output <- torch_argmax(model(b[[1]]$to(device = DEVICE)), 2)\n  target <- b[[2]]$to(device = DEVICE)\n  \n  eval_batch <- data.frame(\n    output = names(train$dataset$class_to_idx[as_array(output$to(device = \"cpu\"))]),\n    target = names(train$dataset$class_to_idx[as_array(target$to(device = \"cpu\"))])  \n  )\n  \n  eval <- rbind(eval, eval_batch)\n})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\neval %>% \n  dplyr::filter(output == target)\nlibrary(dplyr)\nlibrary(alluvial)\nx <-  eval %>%\n  mutate(correct = output == target) %>%\n  count(output, target, correct)\n\nalluvial(\n  x %>% select(target, output),\n  freq = x$n,\n  col = ifelse(x$correct, \"lightblue\", \"red\"),\n  border = ifelse(x$correct, \"lightblue\", \"red\"),\n  alpha = 0.6,\n  hide = x$n < 10\n)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"name":"ir","display_name":"R","language":"R"},"language_info":{"name":"R","codemirror_mode":"r","pygments_lexer":"r","mimetype":"text/x-r-source","file_extension":".r","version":"3.6.3"}},"nbformat":4,"nbformat_minor":4}