From c2776a9c6ed8bec498246035181aa0a6fd1ca084 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Fri, 25 Jul 2025 13:51:58 +0200 Subject: [PATCH 01/10] ci: fail on note --- .github/workflows/rcmdcheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rcmdcheck.yml b/.github/workflows/rcmdcheck.yml index a78f86e74..0bfb68b0b 100644 --- a/.github/workflows/rcmdcheck.yml +++ b/.github/workflows/rcmdcheck.yml @@ -57,4 +57,4 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: - error-on: '"error"' + error-on: '"note"' From c8d214629d724cd6ab8b2325e3c2925ec644656f Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Mon, 28 Jul 2025 13:59:00 +0200 Subject: [PATCH 02/10] remove ::: from fastai learner --- R/learner_fastai_classif_fastai.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index d19cacee7..a3d803a8f 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -164,10 +164,11 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", if (is.null(measure)) measure = fastai::accuracy() # match parameters to fastai functions + fastai2 = getFromNamespace("fastai2", ns = "fastai") args_dt = formalArgs(fastai::TabularDataTable) - args_dl = formalArgs(fastai:::fastai2$data$load$DataLoader) + args_dl = formalArgs(fastai2$data$load$DataLoader) args_config = formalArgs(fastai::tabular_config) - args_fit = formalArgs(fastai:::fastai2$learner$Learner$fit) + args_fit = formalArgs(fastai2$learner$Learner$fit) pv_dt = pars[names(pars) %in% args_dt] pv_dl = pars[names(pars) %in% args_dl] From f599b152e82226ccc033256a0039d876495b6d73 Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Fri, 1 Aug 2025 18:33:27 +0200 Subject: [PATCH 03/10] add numpy assertion to fastai --- R/learner_fastai_classif_fastai.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index 9edb4786b..3eb1edf03 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -149,7 +149,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", .validate = NULL, .train = function(task) { - assert_python_packages("fastai") + assert_python_packages(c("fastai", "numpy")) formula = task$formula() data = task$data() @@ -279,7 +279,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", }, .predict = function(task) { - assert_python_packages("fastai") + assert_python_packages(c("fastai", "numpy")) pars = self$param_set$get_values(tags = "predict") newdata = ordered_features(task, self) From 272c5ad30084f14d8f062b306c8f8a27cb43dca9 Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Fri, 1 Aug 2025 19:02:46 +0200 Subject: [PATCH 04/10] fastai not found? --- R/learner_fastai_classif_fastai.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index 3eb1edf03..dcd1f3a81 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -279,7 +279,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", }, .predict = function(task) { - assert_python_packages(c("fastai", "numpy")) + assert_python_packages("fastai") pars = self$param_set$get_values(tags = "predict") newdata = ordered_features(task, self) From 2b310c2df73e5b05869e26513c6a28eae10c9c36 Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Fri, 1 Aug 2025 19:24:16 +0200 Subject: [PATCH 05/10] check fastai learner --- R/learner_fastai_classif_fastai.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index dcd1f3a81..37ab2911e 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -321,6 +321,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", # Wrapper for eval measure to include in fastai metric = function(pred, dtrain, msr = NULL, lvl = NULL, ...) { reticulate::py_require("fastai") + reticulate::py_require("numpy") # label is a vector of labels (0, 1, ..., n_classes - 1) pred = fastai::as_array(pred) From 99affb92bc69c25e2e7ceeca8cf6e6278865d63f Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Fri, 1 Aug 2025 19:50:33 +0200 Subject: [PATCH 06/10] check fastai learner --- R/learner_fastai_classif_fastai.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index 37ab2911e..cbf99d083 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -149,7 +149,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", .validate = NULL, .train = function(task) { - assert_python_packages(c("fastai", "numpy")) + assert_python_packages("fastai") formula = task$formula() data = task$data() From 4a0374e9b172afe6f74a3ed864d0f773f52db9f2 Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Fri, 1 Aug 2025 20:02:36 +0200 Subject: [PATCH 07/10] fix fastai module availability --- R/learner_fastai_classif_fastai.R | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index cbf99d083..3e733e62d 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -149,7 +149,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", .validate = NULL, .train = function(task) { - assert_python_packages("fastai") + assert_python_packages("fastai", "numpy") formula = task$formula() data = task$data() @@ -279,7 +279,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", }, .predict = function(task) { - assert_python_packages("fastai") + assert_python_packages("fastai", "numpy") pars = self$param_set$get_values(tags = "predict") newdata = ordered_features(task, self) @@ -320,8 +320,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", # Wrapper for eval measure to include in fastai metric = function(pred, dtrain, msr = NULL, lvl = NULL, ...) { - reticulate::py_require("fastai") - reticulate::py_require("numpy") + assert_python_packages("fastai", "numpy") # label is a vector of labels (0, 1, ..., n_classes - 1) pred = fastai::as_array(pred) From 1a17e61df8e3d4f9c435322185dd2ac87eb06c4e Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Fri, 1 Aug 2025 20:34:02 +0200 Subject: [PATCH 08/10] replate assert with py_require in fastai --- R/learner_fastai_classif_fastai.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index 3e733e62d..c4595fe48 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -149,7 +149,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", .validate = NULL, .train = function(task) { - assert_python_packages("fastai", "numpy") + reticulate::py_require("fastai", "numpy") formula = task$formula() data = task$data() @@ -279,7 +279,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", }, .predict = function(task) { - assert_python_packages("fastai", "numpy") + reticulate::py_require("fastai", "numpy") pars = self$param_set$get_values(tags = "predict") newdata = ordered_features(task, self) @@ -320,7 +320,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", # Wrapper for eval measure to include in fastai metric = function(pred, dtrain, msr = NULL, lvl = NULL, ...) { - assert_python_packages("fastai", "numpy") + reticulate::py_require("fastai", "numpy") # label is a vector of labels (0, 1, ..., n_classes - 1) pred = fastai::as_array(pred) From e446bafa9c0e0dd901dd71da473a3407ecbdfe94 Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Tue, 5 Aug 2025 20:38:36 +0200 Subject: [PATCH 09/10] refactor fastai tests with callr --- R/learner_fastai_classif_fastai.R | 6 +- tests/testthat/test_fastai_classif_fastai.R | 455 +++++++++++--------- 2 files changed, 260 insertions(+), 201 deletions(-) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index c4595fe48..78be7a391 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -149,7 +149,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", .validate = NULL, .train = function(task) { - reticulate::py_require("fastai", "numpy") + assert_python_packages(c("fastai", "numpy")) formula = task$formula() data = task$data() @@ -279,7 +279,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", }, .predict = function(task) { - reticulate::py_require("fastai", "numpy") + assert_python_packages(c("fastai", "numpy")) pars = self$param_set$get_values(tags = "predict") newdata = ordered_features(task, self) @@ -320,7 +320,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", # Wrapper for eval measure to include in fastai metric = function(pred, dtrain, msr = NULL, lvl = NULL, ...) { - reticulate::py_require("fastai", "numpy") + assert_python_packages(c("fastai", "numpy")) # label is a vector of labels (0, 1, ..., n_classes - 1) pred = fastai::as_array(pred) diff --git a/tests/testthat/test_fastai_classif_fastai.R b/tests/testthat/test_fastai_classif_fastai.R index b87a09ed1..128d95ecf 100644 --- a/tests/testthat/test_fastai_classif_fastai.R +++ b/tests/testthat/test_fastai_classif_fastai.R @@ -1,214 +1,273 @@ test_that("autotest", { - learner = lrn("classif.fastai", layers = c(200, 100)) - expect_learner(learner, check_man = FALSE) - # results not replicable, because torch seed must be set in the python backend - result = run_autotest(learner, check_replicable = FALSE) - expect_true(result, info = result$error) + expect_true(callr::r(function() { + Sys.setenv(RETICULATE_PYTHON = "managed") + library(mlr3) + library(mlr3extralearners) + + lapply(list.files(system.file("testthat", package = "mlr3"), + pattern = "^helper.*\\.[rR]", full.names = TRUE), source) + + reticulate::py_require(c("numpy", "fastai"), python_version = "3.10") + + learner = lrn("classif.fastai", layers = c(200, 100)) + expect_learner(learner, check_man = FALSE) + # results not replicable, because torch seed must be set in the python backend + result = run_autotest(learner, check_replicable = FALSE) + testthat::expect_true(result, info = result$error) + TRUE + })) }) test_that("eval protocol", { - learner = lrn("classif.fastai") - task = tsk("sonar") - learner$train(task) - expect_true(is.list(learner$state$eval_protocol)) + expect_true(callr::r(function() { + Sys.setenv(RETICULATE_PYTHON = "managed") + library(mlr3) + library(mlr3extralearners) + + lapply(list.files(system.file("testthat", package = "mlr3"), + pattern = "^helper.*\\.[rR]", full.names = TRUE), source) + + reticulate::py_require(c("numpy", "fastai"), python_version = "3.10") + + learner = lrn("classif.fastai") + task = tsk("sonar") + learner$train(task) + expect_true(is.list(learner$state$eval_protocol)) + TRUE + })) }) test_that("validation and inner tuning works", { - task = tsk("spam") - - # with n_epoch and patience parameter - learner = lrn("classif.fastai", - n_epoch = 10, - patience = 1, - validate = 0.2 - ) - - learner$train(task) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "accuracy")) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "accuracy") - expect_list(learner$internal_tuned_values, types = "integerish") - expect_equal(names(learner$internal_tuned_values), "n_epoch") - - # without validate parameter - learner$validate = NULL - expect_error(learner$train(task), "field 'validate'") - - # with patience parameter - learner = lrn("classif.fastai", - n_epoch = 10, - validate = 0.2 - ) - - learner$train(task) - expect_equal(learner$internal_tuned_values, NULL) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "accuracy")) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "accuracy") - - # internal tuning - learner = lrn("classif.fastai", - n_epoch = to_tune(upper = 20, internal = TRUE), - validate = 0.2 - ) - s = learner$param_set$search_space() - expect_error(learner$param_set$convert_internal_search_space(s), "patience") - learner$param_set$set_values(n_epoch = 10) - learner$param_set$disable_internal_tuning("n_epoch") - expect_equal(learner$param_set$values$n_epoch, NULL) - - learner = lrn("classif.fastai", - n_epoch = 20, - patience = 5, - validate = 0.3 - ) - learner$train(task) - expect_equal(learner$internal_valid_scores$accuracy, learner$state$eval_protocol$accuracy[learner$internal_tuned_values$n_epoch]) - - # no validation and no internal tuning - learner = lrn("classif.fastai") - learner$train(task) - expect_null(learner$internal_valid_scores) - expect_null(learner$internal_tuned_values) - - # no tuning without patience parameter - learner = lrn("classif.fastai", validate = 0.3, n_epoch = 10) - learner$train(task) - expect_equal(learner$internal_valid_scores$accuracy, learner$state$eval_protocol$accuracy[10L]) - expect_null(learner$internal_tuned_values) + expect_true(callr::r(function() { + Sys.setenv(RETICULATE_PYTHON = "managed") + library(mlr3) + library(mlr3extralearners) + + lapply(list.files(system.file("testthat", package = "mlr3"), + pattern = "^helper.*\\.[rR]", full.names = TRUE), source) + + reticulate::py_require(c("numpy", "fastai"), python_version = "3.10") + + task = tsk("spam") + + # with n_epoch and patience parameter + learner = lrn("classif.fastai", + n_epoch = 10, + patience = 1, + validate = 0.2 + ) + + learner$train(task) + expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "accuracy")) + expect_list(learner$internal_valid_scores, types = "numeric") + expect_equal(names(learner$internal_valid_scores), "accuracy") + expect_list(learner$internal_tuned_values, types = "integerish") + expect_equal(names(learner$internal_tuned_values), "n_epoch") + + # without validate parameter + learner$validate = NULL + expect_error(learner$train(task), "field 'validate'") + + # with patience parameter + learner = lrn("classif.fastai", + n_epoch = 10, + validate = 0.2 + ) + + learner$train(task) + expect_equal(learner$internal_tuned_values, NULL) + expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "accuracy")) + expect_list(learner$internal_valid_scores, types = "numeric") + expect_equal(names(learner$internal_valid_scores), "accuracy") + + # internal tuning + learner = lrn("classif.fastai", + n_epoch = to_tune(upper = 20, internal = TRUE), + validate = 0.2 + ) + s = learner$param_set$search_space() + expect_error(learner$param_set$convert_internal_search_space(s), "patience") + learner$param_set$set_values(n_epoch = 10) + learner$param_set$disable_internal_tuning("n_epoch") + expect_equal(learner$param_set$values$n_epoch, NULL) + + learner = lrn("classif.fastai", + n_epoch = 20, + patience = 5, + validate = 0.3 + ) + learner$train(task) + expect_equal(learner$internal_valid_scores$accuracy, learner$state$eval_protocol$accuracy[learner$internal_tuned_values$n_epoch]) + + # no validation and no internal tuning + learner = lrn("classif.fastai") + learner$train(task) + expect_null(learner$internal_valid_scores) + expect_null(learner$internal_tuned_values) + + # no tuning without patience parameter + learner = lrn("classif.fastai", validate = 0.3, n_epoch = 10) + learner$train(task) + expect_equal(learner$internal_valid_scores$accuracy, learner$state$eval_protocol$accuracy[10L]) + expect_null(learner$internal_tuned_values) + TRUE + })) }) test_that("custom inner validation measure", { - # internal measure - task = tsk("sonar") - - learner = lrn("classif.fastai", - n_epoch = 10, - validate = 0.2, - patience = 1, - eval_metric = fastai::error_rate() - ) - - learner$train(task) - - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "error_rate")) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "error_rate") - - # binary task and mlr3 measure binary response - task = tsk("sonar") - - learner = lrn("classif.fastai", - n_epoch = 10, - validate = 0.2, - #patience = 1, - eval_metric = msr("classif.ce") - ) - - learner$train(task) - - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.ce")) - expect_numeric(learner$state$eval_protocol$classif.ce, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.ce") - - # binary task and mlr3 measure binary prob - task = tsk("sonar") - - learner = lrn("classif.fastai", - n_epoch = 10, - validate = 0.2, - #patience = 1, - predict_type = "prob", - eval_metric = msr("classif.logloss") - ) - - learner$train(task) - - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.logloss")) - expect_numeric(learner$state$eval_protocol$classif.logloss, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.logloss") - - # binary task and mlr3 measure multiclass prob - task = tsk("sonar") - - learner = lrn("classif.fastai", - n_epoch = 10, - validate = 0.2, - # patience = 1, - predict_type = "prob", - eval_metric = msr("classif.auc") - ) - - learner$train(task) - - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.auc")) - expect_numeric(learner$state$eval_protocol$classif.auc, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.auc") - - # multiclass task and mlr3 measure multiclass response - task = tsk("iris") - - learner = lrn("classif.fastai", - n_epoch = 10, - validate = 0.2, - #patience = 1, - predict_type = "prob", - eval_metric = msr("classif.ce") - ) - - learner$train(task) - - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.ce")) - expect_numeric(learner$state$eval_protocol$classif.ce, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.ce") - - # multiclass task and mlr3 measure multiclass prob - task = tsk("iris") - - learner = lrn("classif.fastai", - n_epoch = 10, - validate = 0.2, - # patience = 1, - predict_type = "prob", - eval_metric = msr("classif.logloss") - ) - - learner$train(task) - - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.logloss")) - expect_numeric(learner$state$eval_protocol$classif.logloss, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.logloss") + expect_true(callr::r(function() { + Sys.setenv(RETICULATE_PYTHON = "managed") + library(mlr3) + library(mlr3extralearners) + + lapply(list.files(system.file("testthat", package = "mlr3"), + pattern = "^helper.*\\.[rR]", full.names = TRUE), source) + + reticulate::py_require(c("numpy", "fastai"), python_version = "3.10") + # internal measure + task = tsk("sonar") + + learner = lrn("classif.fastai", + n_epoch = 10, + validate = 0.2, + patience = 1, + eval_metric = fastai::error_rate() + ) + + learner$train(task) + + expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "error_rate")) + expect_list(learner$internal_valid_scores, types = "numeric") + expect_equal(names(learner$internal_valid_scores), "error_rate") + + # binary task and mlr3 measure binary response + task = tsk("sonar") + + learner = lrn("classif.fastai", + n_epoch = 10, + validate = 0.2, + #patience = 1, + eval_metric = msr("classif.ce") + ) + + learner$train(task) + + expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.ce")) + expect_numeric(learner$state$eval_protocol$classif.ce, len = 10) + expect_list(learner$internal_valid_scores, types = "numeric") + expect_equal(names(learner$internal_valid_scores), "classif.ce") + + # binary task and mlr3 measure binary prob + task = tsk("sonar") + + learner = lrn("classif.fastai", + n_epoch = 10, + validate = 0.2, + #patience = 1, + predict_type = "prob", + eval_metric = msr("classif.logloss") + ) + + learner$train(task) + + expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.logloss")) + expect_numeric(learner$state$eval_protocol$classif.logloss, len = 10) + expect_list(learner$internal_valid_scores, types = "numeric") + expect_equal(names(learner$internal_valid_scores), "classif.logloss") + + # binary task and mlr3 measure multiclass prob + task = tsk("sonar") + + learner = lrn("classif.fastai", + n_epoch = 10, + validate = 0.2, + # patience = 1, + predict_type = "prob", + eval_metric = msr("classif.auc") + ) + + learner$train(task) + + expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.auc")) + expect_numeric(learner$state$eval_protocol$classif.auc, len = 10) + expect_list(learner$internal_valid_scores, types = "numeric") + expect_equal(names(learner$internal_valid_scores), "classif.auc") + + # multiclass task and mlr3 measure multiclass response + task = tsk("iris") + + learner = lrn("classif.fastai", + n_epoch = 10, + validate = 0.2, + #patience = 1, + predict_type = "prob", + eval_metric = msr("classif.ce") + ) + + learner$train(task) + + expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.ce")) + expect_numeric(learner$state$eval_protocol$classif.ce, len = 10) + expect_list(learner$internal_valid_scores, types = "numeric") + expect_equal(names(learner$internal_valid_scores), "classif.ce") + + # multiclass task and mlr3 measure multiclass prob + task = tsk("iris") + + learner = lrn("classif.fastai", + n_epoch = 10, + validate = 0.2, + # patience = 1, + predict_type = "prob", + eval_metric = msr("classif.logloss") + ) + + learner$train(task) + + expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.logloss")) + expect_numeric(learner$state$eval_protocol$classif.logloss, len = 10) + expect_list(learner$internal_valid_scores, types = "numeric") + expect_equal(names(learner$internal_valid_scores), "classif.logloss") + TRUE + })) }) test_that("marshaling works for classif.fastai", { - learner = lrn("classif.fastai") - task = tsk("iris") - # expect_marshalable_learner(learner, task) - - learner$train(task) - pred = learner$predict(task) - model = learner$model - class_prev = class(model) - - # checks for marshaling is the same as expect_marshalable_learner - expect_false(learner$marshaled) - expect_equal(is_marshaled_model(learner$model), learner$marshaled) - expect_invisible(learner$marshal()) - expect_equal(mlr3::is_marshaled_model(learner$model), learner$marshaled) - - # checks for unmarshaling differs -- instead of checking equality of model, - # we check equality of predictions, because expect_equal() on python objects - # checks the pointer which almost always changes after unmarshaling - expect_invisible(learner$unmarshal()) - expect_prediction(learner$predict(task)) - expect_equal(learner$predict(task), pred) - expect_false(learner$marshaled) - expect_equal(class(learner$model), class_prev) + expect_true(callr::r(function() { + Sys.setenv(RETICULATE_PYTHON = "managed") + library(mlr3) + library(mlr3extralearners) + + lapply(list.files(system.file("testthat", package = "mlr3"), + pattern = "^helper.*\\.[rR]", full.names = TRUE), source) + + reticulate::py_require(c("numpy", "fastai"), python_version = "3.10") + + learner = lrn("classif.fastai") + task = tsk("iris") + # expect_marshalable_learner(learner, task) + + learner$train(task) + pred = learner$predict(task) + model = learner$model + class_prev = class(model) + + # checks for marshaling is the same as expect_marshalable_learner + expect_false(learner$marshaled) + expect_equal(is_marshaled_model(learner$model), learner$marshaled) + expect_invisible(learner$marshal()) + expect_equal(mlr3::is_marshaled_model(learner$model), learner$marshaled) + + # checks for unmarshaling differs -- instead of checking equality of model, + # we check equality of predictions, because expect_equal() on python objects + # checks the pointer which almost always changes after unmarshaling + expect_invisible(learner$unmarshal()) + expect_prediction(learner$predict(task)) + expect_equal(learner$predict(task), pred) + expect_false(learner$marshaled) + expect_equal(class(learner$model), class_prev) + TRUE + })) }) From eee6c98c6aab22bf54137be1faa9e69268418c85 Mon Sep 17 00:00:00 2001 From: AnnaNzrv Date: Wed, 6 Aug 2025 20:50:49 +0200 Subject: [PATCH 10/10] fix fastai tests and delay numpy import in fastai --- R/learner_fastai_classif_fastai.R | 2 +- tests/testthat/test_fastai_classif_fastai.R | 102 ++++++++++---------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/R/learner_fastai_classif_fastai.R b/R/learner_fastai_classif_fastai.R index 78be7a391..227f11a0a 100644 --- a/R/learner_fastai_classif_fastai.R +++ b/R/learner_fastai_classif_fastai.R @@ -249,7 +249,7 @@ LearnerClassifFastai = R6Class("LearnerClassifFastai", # direction for internal tuning # if mlr3 measure and minimize use numpy less comp = if (inherits(measure, "Measure") && measure$minimize) { - np = reticulate::import("numpy") + np = reticulate::import("numpy", delay_load = TRUE) np$less } diff --git a/tests/testthat/test_fastai_classif_fastai.R b/tests/testthat/test_fastai_classif_fastai.R index 128d95ecf..a667f847d 100644 --- a/tests/testthat/test_fastai_classif_fastai.R +++ b/tests/testthat/test_fastai_classif_fastai.R @@ -1,3 +1,5 @@ +skip_on_os("windows") + test_that("autotest", { expect_true(callr::r(function() { Sys.setenv(RETICULATE_PYTHON = "managed") @@ -32,7 +34,7 @@ test_that("eval protocol", { learner = lrn("classif.fastai") task = tsk("sonar") learner$train(task) - expect_true(is.list(learner$state$eval_protocol)) + testthat::expect_true(is.list(learner$state$eval_protocol)) TRUE })) }) @@ -59,15 +61,15 @@ test_that("validation and inner tuning works", { ) learner$train(task) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "accuracy")) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "accuracy") - expect_list(learner$internal_tuned_values, types = "integerish") - expect_equal(names(learner$internal_tuned_values), "n_epoch") + testthat::expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "accuracy")) + testthat::expect_list(learner$internal_valid_scores, types = "numeric") + testthat::expect_equal(names(learner$internal_valid_scores), "accuracy") + testthat::expect_list(learner$internal_tuned_values, types = "integerish") + testthat::expect_equal(names(learner$internal_tuned_values), "n_epoch") # without validate parameter learner$validate = NULL - expect_error(learner$train(task), "field 'validate'") + testthat::expect_error(learner$train(task), "field 'validate'") # with patience parameter learner = lrn("classif.fastai", @@ -76,10 +78,10 @@ test_that("validation and inner tuning works", { ) learner$train(task) - expect_equal(learner$internal_tuned_values, NULL) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "accuracy")) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "accuracy") + testthat::expect_equal(learner$internal_tuned_values, NULL) + testthat::expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "accuracy")) + testthat::expect_list(learner$internal_valid_scores, types = "numeric") + testthat::expect_equal(names(learner$internal_valid_scores), "accuracy") # internal tuning learner = lrn("classif.fastai", @@ -87,10 +89,10 @@ test_that("validation and inner tuning works", { validate = 0.2 ) s = learner$param_set$search_space() - expect_error(learner$param_set$convert_internal_search_space(s), "patience") + testthat::expect_error(learner$param_set$convert_internal_search_space(s), "patience") learner$param_set$set_values(n_epoch = 10) learner$param_set$disable_internal_tuning("n_epoch") - expect_equal(learner$param_set$values$n_epoch, NULL) + testthat::expect_equal(learner$param_set$values$n_epoch, NULL) learner = lrn("classif.fastai", n_epoch = 20, @@ -98,19 +100,19 @@ test_that("validation and inner tuning works", { validate = 0.3 ) learner$train(task) - expect_equal(learner$internal_valid_scores$accuracy, learner$state$eval_protocol$accuracy[learner$internal_tuned_values$n_epoch]) + testthat::expect_equal(learner$internal_valid_scores$accuracy, learner$state$eval_protocol$accuracy[learner$internal_tuned_values$n_epoch]) # no validation and no internal tuning learner = lrn("classif.fastai") learner$train(task) - expect_null(learner$internal_valid_scores) - expect_null(learner$internal_tuned_values) + testthat::expect_null(learner$internal_valid_scores) + testthat::expect_null(learner$internal_tuned_values) # no tuning without patience parameter learner = lrn("classif.fastai", validate = 0.3, n_epoch = 10) learner$train(task) - expect_equal(learner$internal_valid_scores$accuracy, learner$state$eval_protocol$accuracy[10L]) - expect_null(learner$internal_tuned_values) + testthat::expect_equal(learner$internal_valid_scores$accuracy, learner$state$eval_protocol$accuracy[10L]) + testthat::expect_null(learner$internal_tuned_values) TRUE })) }) @@ -137,9 +139,9 @@ test_that("custom inner validation measure", { learner$train(task) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "error_rate")) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "error_rate") + testthat::expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "error_rate")) + testthat::expect_list(learner$internal_valid_scores, types = "numeric") + testthat::expect_equal(names(learner$internal_valid_scores), "error_rate") # binary task and mlr3 measure binary response task = tsk("sonar") @@ -153,10 +155,10 @@ test_that("custom inner validation measure", { learner$train(task) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.ce")) - expect_numeric(learner$state$eval_protocol$classif.ce, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.ce") + testthat::expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.ce")) + testthat::expect_numeric(learner$state$eval_protocol$classif.ce, len = 10) + testthat::expect_list(learner$internal_valid_scores, types = "numeric") + testthat::expect_equal(names(learner$internal_valid_scores), "classif.ce") # binary task and mlr3 measure binary prob task = tsk("sonar") @@ -171,10 +173,10 @@ test_that("custom inner validation measure", { learner$train(task) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.logloss")) - expect_numeric(learner$state$eval_protocol$classif.logloss, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.logloss") + testthat::expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.logloss")) + testthat::expect_numeric(learner$state$eval_protocol$classif.logloss, len = 10) + testthat::expect_list(learner$internal_valid_scores, types = "numeric") + testthat::expect_equal(names(learner$internal_valid_scores), "classif.logloss") # binary task and mlr3 measure multiclass prob task = tsk("sonar") @@ -189,10 +191,10 @@ test_that("custom inner validation measure", { learner$train(task) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.auc")) - expect_numeric(learner$state$eval_protocol$classif.auc, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.auc") + testthat::expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.auc")) + testthat::expect_numeric(learner$state$eval_protocol$classif.auc, len = 10) + testthat::expect_list(learner$internal_valid_scores, types = "numeric") + testthat::expect_equal(names(learner$internal_valid_scores), "classif.auc") # multiclass task and mlr3 measure multiclass response task = tsk("iris") @@ -207,10 +209,10 @@ test_that("custom inner validation measure", { learner$train(task) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.ce")) - expect_numeric(learner$state$eval_protocol$classif.ce, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.ce") + testthat::expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.ce")) + testthat::expect_numeric(learner$state$eval_protocol$classif.ce, len = 10) + testthat::expect_list(learner$internal_valid_scores, types = "numeric") + testthat::expect_equal(names(learner$internal_valid_scores), "classif.ce") # multiclass task and mlr3 measure multiclass prob task = tsk("iris") @@ -225,10 +227,10 @@ test_that("custom inner validation measure", { learner$train(task) - expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.logloss")) - expect_numeric(learner$state$eval_protocol$classif.logloss, len = 10) - expect_list(learner$internal_valid_scores, types = "numeric") - expect_equal(names(learner$internal_valid_scores), "classif.logloss") + testthat::expect_named(learner$state$eval_protocol, c("epoch", "train_loss", "valid_loss", "classif.logloss")) + testthat::expect_numeric(learner$state$eval_protocol$classif.logloss, len = 10) + testthat::expect_list(learner$internal_valid_scores, types = "numeric") + testthat::expect_equal(names(learner$internal_valid_scores), "classif.logloss") TRUE })) }) @@ -254,19 +256,19 @@ test_that("marshaling works for classif.fastai", { class_prev = class(model) # checks for marshaling is the same as expect_marshalable_learner - expect_false(learner$marshaled) - expect_equal(is_marshaled_model(learner$model), learner$marshaled) - expect_invisible(learner$marshal()) - expect_equal(mlr3::is_marshaled_model(learner$model), learner$marshaled) + testthat::expect_false(learner$marshaled) + testthat::expect_equal(is_marshaled_model(learner$model), learner$marshaled) + testthat::expect_invisible(learner$marshal()) + testthat::expect_equal(mlr3::is_marshaled_model(learner$model), learner$marshaled) # checks for unmarshaling differs -- instead of checking equality of model, # we check equality of predictions, because expect_equal() on python objects # checks the pointer which almost always changes after unmarshaling - expect_invisible(learner$unmarshal()) - expect_prediction(learner$predict(task)) - expect_equal(learner$predict(task), pred) - expect_false(learner$marshaled) - expect_equal(class(learner$model), class_prev) + testthat::expect_invisible(learner$unmarshal()) + testthat::expect_prediction(learner$predict(task)) + testthat::expect_equal(learner$predict(task), pred) + testthat::expect_false(learner$marshaled) + testthat::expect_equal(class(learner$model), class_prev) TRUE })) })