Skip to content

Benchmark/rf use case #294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 39 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
2380913
TODO: write tests
cxzhang4 Sep 10, 2024
86f87c8
name -> TB. began refactoring based on last meeting with Sebastian
cxzhang4 Sep 22, 2024
400ed74
slight description change
cxzhang4 Oct 2, 2024
9e6acd8
removed extraneous comments
cxzhang4 Oct 2, 2024
fc4f2fa
added n_last_loss frequency test
cxzhang4 Oct 2, 2024
81d1ded
in progress
cxzhang4 Oct 10, 2024
cb03eb3
autotest working, accidentally used the wrong callback_generator
cxzhang4 Oct 10, 2024
78b95a5
simple and eval_freq tests pass
cxzhang4 Oct 11, 2024
a365757
changed logging methods to private
cxzhang4 Oct 11, 2024
43a8ffb
removed magrittr pipe from tests
cxzhang4 Oct 11, 2024
6b9a845
added details for callback class
cxzhang4 Oct 11, 2024
d354b2c
formatting
cxzhang4 Oct 11, 2024
b5b27b1
built docs
cxzhang4 Oct 11, 2024
565456b
Merge branch 'main' into feat/tflog-callback
cxzhang4 Oct 11, 2024
7c9f431
all tests pass, I think this is parity with the previous broken commi…
cxzhang4 Oct 11, 2024
c6c9333
implemented step logging
cxzhang4 Oct 11, 2024
43e7396
removed extraneous comments
cxzhang4 Oct 11, 2024
ec5d8fc
added tensorboard instructions
cxzhang4 Oct 11, 2024
f26a254
passes R CMD Check, minimally addresses every comment in the previous PR
cxzhang4 Oct 11, 2024
a86c946
moved newest news to bottom
cxzhang4 Oct 13, 2024
3652fe6
init
cxzhang4 Oct 15, 2024
92b4ffc
Update benchmarks/rf_use_case/run_benchmark.R
cxzhang4 Oct 15, 2024
f821e09
use mlr3oml cache
cxzhang4 Oct 17, 2024
5903001
Copied in Sebastian's solution for tuning the neurons as a paramset
cxzhang4 Oct 17, 2024
869aba2
looks like benchmark code working
cxzhang4 Oct 17, 2024
ab3bedf
Error: Inner tuning and parameter transformations are currently not s…
cxzhang4 Oct 17, 2024
31b3964
changed to grid search
cxzhang4 Oct 17, 2024
a489897
LLM-generated fn for neuron search space
cxzhang4 Oct 17, 2024
0073dcc
should work, test this on another machine
cxzhang4 Oct 17, 2024
10f3448
fjwoie
cxzhang4 Oct 18, 2024
b81c23b
encapsulated the learner for parallelization
cxzhang4 Oct 18, 2024
00b272f
comments
cxzhang4 Oct 18, 2024
89a72f1
added install script
cxzhang4 Oct 20, 2024
52af8ed
looks ready to run. 100 evals of mbo
cxzhang4 Oct 22, 2024
95f0a45
addoed surrogate learner for mbo
cxzhang4 Oct 22, 2024
5c0a447
Delete R/CallbackSetTB.R
sebffischer Feb 6, 2025
c384529
Delete tests/testthat/test_CallbackSetTB.R
sebffischer Feb 6, 2025
ee3f51d
merge main
sebffischer Feb 6, 2025
c01c531
update benchmark
sebffischer Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ inst/doc
/doc/
/Meta/
CRAN-SUBMISSION
benchmarks/data
9 changes: 8 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ Authors@R:
family = "Pfisterer",
role = "ctb",
email = "[email protected]",
comment = c(ORCID = "0000-0001-8867-762X")))
comment = c(ORCID = "0000-0001-8867-762X")),
person(given = "Carson",
family = "Zhang",
role = "ctb",
email = "[email protected]")
)
Description: Deep Learning library that extends the mlr3 framework by building
upon the 'torch' package. It allows to conveniently build, train,
and evaluate deep learning models without having to worry about low level
Expand Down Expand Up @@ -64,6 +69,7 @@ Suggests:
viridis,
visNetwork,
testthat (>= 3.0.0),
tfevents,
torchvision (>= 0.6.0),
waldo
Config/testthat/edition: 3
Expand All @@ -80,6 +86,7 @@ Collate:
'CallbackSetEarlyStopping.R'
'CallbackSetHistory.R'
'CallbackSetProgress.R'
'CallbackSetTB.R'
'ContextTorch.R'
'DataBackendLazy.R'
'utils.R'
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export(CallbackSet)
export(CallbackSetCheckpoint)
export(CallbackSetHistory)
export(CallbackSetProgress)
export(CallbackSetTB)
export(ContextTorch)
export(DataBackendLazy)
export(DataDescriptor)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# mlr3torch dev

* Don't use deprecated `data_formats` anymore
* Added `CallbackSetTB`, which allows logging that can be viewed by TensorBoard.

# mlr3torch 0.1.1

Expand Down
85 changes: 85 additions & 0 deletions R/CallbackSetTB.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#' @title TensorBoard Logging Callback
#'
#' @name mlr_callback_set.tb
#'
#' @description
#' Logs training loss, training measures, and validation measures as events.
#' To view them, use TensorBoard with `tensorflow::tensorboard()` (requires `tensorflow`) or the CLI.
#' @details
#' Logs events at most every epoch.
#'
#' @param path (`character(1)`)\cr
#' The path to a folder where the events are logged.
#' Point TensorBoard to this folder to view them.
#' @param log_train_loss (`logical(1)`)\cr
#' Whether we log the training loss.
#' @family Callback
#' @export
#' @include CallbackSet.R
CallbackSetTB = R6Class("CallbackSetTB",
inherit = CallbackSet,
lock_objects = FALSE,
public = list(
#' @description
#' Creates a new instance of this [R6][R6::R6Class] class.
initialize = function(path, log_train_loss) {
self$path = assert_path_for_output(path)
if (!dir.exists(path)) {
dir.create(path, recursive = TRUE)
}
self$log_train_loss = assert_logical(log_train_loss)
},
#' @description
#' Logs the training loss, training measures, and validation measures as TensorFlow events.
on_epoch_end = function() {
if (self$log_train_loss) {
private$.log_train_loss()
}

if (length(self$ctx$last_scores_train)) {
map(names(self$ctx$measures_train), private$.log_train_score)
}

if (length(self$ctx$last_scores_valid)) {
map(names(self$ctx$measures_valid), private$.log_valid_score)
}
}
),
private = list(
.log_score = function(prefix, measure_name, score) {
event_list = list(score, self$ctx$epoch)
names(event_list) = c(paste0(prefix, measure_name), "step")

with_logdir(self$path, {
do.call(log_event, event_list)
})
},
.log_valid_score = function(measure_name) {
valid_score = self$ctx$last_scores_valid[[measure_name]]
private$.log_score("valid.", measure_name, valid_score)
},
.log_train_score = function(measure_name) {
train_score = self$ctx$last_scores_train[[measure_name]]
private$.log_score("train.", measure_name, train_score)
},
.log_train_loss = function() {
with_logdir(self$path, {
log_event(train.loss = self$ctx$last_loss)
})
}
)
)

#' @include TorchCallback.R
mlr3torch_callbacks$add("tb", function() {
TorchCallback$new(
callback_generator = CallbackSetTB,
param_set = ps(
path = p_uty(tags = c("train", "required")),
log_train_loss = p_lgl(tags = c("train", "required"))
),
id = "tb",
label = "TensorBoard",
man = "mlr3torch::mlr_callback_set.tb"
)
})
28 changes: 28 additions & 0 deletions benchmarks/rf_use_case/get_data.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
library(here)

library(mlr3oml)
library(data.table)
library(tidytable)

cc18_collection = ocl(99)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cc18_collection = ocl(99)
options(mlr3oml.cache = TRUE)
cc18_collection = ocl(99)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also add this to your .Rprofile


cc18_simple = list_oml_data(data_id = cc18_collection$data_ids,
number_classes = 2,
number_missing_values = 0)

cc18_small = cc18_simple |>
filter(NumberOfSymbolicFeatures == 1) |>
select(data_id, name, NumberOfFeatures, NumberOfInstances) |>
filter(name %in% c("qsar-biodeg", "madelon", "kc1", "blood-transfusion-service-center", "climate-model-simulation-crashes"))

# kc1_1067 = odt(1067)


# save the data locally
mlr3misc::pmap(cc18_small, function(data_id, name, NumberOfFeatures, NumberOfInstances) {
dt = odt(data_id)$data
dt_name = here("data", "oml", paste0(name, "_", data_id, ".csv"))
fwrite(dt, file = dt_name)
})

fwrite(cc18_small, here("data", "oml", "cc18_small.csv"))
105 changes: 105 additions & 0 deletions benchmarks/rf_use_case/run_benchmark.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
library(mlr3)
library(data.table)
library(mlr3torch)
library(paradox)

library(here)

# define the tasks
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsk("oml", task_id = 1067)
similarly hard-code for every task

this will ignore the OpenML resampling

cc18_small = fread(here("data", "oml", "cc18_small.csv"))
cc18_small_datasets = mlr3misc::pmap(cc18_small, function(data_id, name, NumberOfFeatures, NumberOfInstances) {
dt_name = here("data", "oml", paste0(name, "_", data_id, ".csv"))
fread(dt_name)
})
# cc18_small_datasets

# cc18_small_datasets[[1]]

# TODO: determine whether we can use OML tasks "directly"
# didn't do this at first because they come with resamplings and we want to use our own resamplings
kc1_1067 = as_task_classif(cc18_small_datasets[[1]], target = "defects")
blood_1464 = as_task_classif(cc18_small_datasets[[2]], target = "Class")

tasks = list(kc1_1067, blood_1464)

# define the learners
mlp = lrn("classif.mlp",
activation = nn_relu,
neurons = to_tune(
Copy link
Member

@sebffischer sebffischer Oct 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
neurons = to_tune(
neurons = to_tune(ps(n_layers = p_int(lower = 1, upper = 10), latent = p_int(10, 500), .extra_trafo = function(x, param_set) {
list(neurons = rep(x$latent, x$n_layers))
}))

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this won't work because the libraries don't allow parameter transformations. When I try to run the experiment I get this error:

Error: Inner tuning and parameter transformations are currently not supported.

Copy link
Collaborator Author

@cxzhang4 cxzhang4 Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My solution for now:

n_layers_values <- 1:10
latent_dim_values <- seq(10, 500, by = 10)
neurons_search_space <- mapply(
  neurons,
  expand.grid(n_layers = n_layers_values, latent_dim = latent_dim_values)$n_layers,
  expand.grid(n_layers = n_layers_values, latent_dim = latent_dim_values)$latent_dim,
  SIMPLIFY = FALSE
)

mlp = lrn("classif.mlp",
  activation = nn_relu,
  # neurons = to_tune(ps(
  #   n_layers = p_int(lower = 1, upper = 10), latent = p_int(10, 500),
  #   .extra_trafo = function(x, param_set) {
  #     list(neurons = rep(x$latent, x$n_layers))
  #   })
  # ),
  neurons = to_tune(neurons_search_space)

c(
10, 20,
c(10, 10), c(10, 20), c(20, 10), c(20, 20)
)
),
batch_size = to_tune(16, 32, 64),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go bigger

Suggested change
batch_size = to_tune(16, 32, 64),
batch_size = to_tune(16, 32, 64, 128, 256),

p = to_tune(0.1, 0.9),
epochs = to_tune(upper = 1000L, internal = TRUE),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider reducing the max number of epochs

validate = 0.3,
measures_valid = msr("classif.acc"),
patience = 10,
device = "cpu"
)

# define an AutoTuner that wraps the classif.mlp
at = auto_tuner(
learner = mlp,
tuner = tnr("grid_search"),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use MBO: more efficient

resampling = rsmp("cv"),
measure = msr("clasif.acc"),
term_evals = 10
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likely need more than 10

Suggested change
term_evals = 10
term_evals = 100

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run on GPU server (but without all cores)

)

future::plan("multisession")

design = benchmark_grid(
tasks,
learners = list(at, lrn("classif.ranger"),
resampling = rsmp("cv", folds = 10))
)

bmr = benchmark(design)

bmrdt = as.data.table(bmr)

fwrite(bmrdt, here("R", "rf_Use_case", "results", "bmrdt.csv"))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Suggested change
fwrite(bmrdt, here("R", "rf_Use_case", "results", "bmrdt.csv"))
fwrite(bmrdt, here("R", "rf_use_case", "results", "bmrdt.csv"))


# define an optimization strategy: grid search

# define a search space: the parameters to tune over

# neurons

# batch size

# dropout rate

# epochs

# use something standard (e.g. accuracy) as the tuning measure

# use k-fold cross validation

# set a number of evaluations for the tuner

# TODO: set up the tuning space for the neurons and layers

# layers_search_space <- 1:5
# neurons_search_space <- seq(10, 50, by = 10)

# generate_permutations <- function(layers_search_space, neurons_search_space) {
# result <- list()

# for (layers in layers_search_space) {
# # Generate all permutations with replacement
# perms <- expand.grid(replicate(layers, list(neurons_search_space), simplify = FALSE))

# # Convert each row to a vector and add to the result
# result <- c(result, apply(perms, 1, as.numeric))
# }

# return(result)
# }

# permutations <- generate_permutations(layers_search_space, neurons_search_space)

# head(permutations)
8 changes: 8 additions & 0 deletions benchmarks/rf_use_case/view_results.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
library(data.table)
library(mlr3)

library(here)

bmrdt = fread(here("R", "rf_Use_case", "results", "bmrdt.csv"))

bmrdt
1 change: 1 addition & 0 deletions man/TorchCallback.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/as_torch_callback.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/as_torch_callbacks.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/callback_set.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/mlr3torch-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/mlr3torch_callbacks.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/mlr_callback_set.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/mlr_callback_set.checkpoint.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/mlr_callback_set.progress.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading